Put some OSX code into gtk version
[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 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 \r
97 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
98 \r
99   int myrandom(void);\r
100   void mysrandom(unsigned int seed);\r
101 \r
102 extern int whiteFlag, blackFlag;\r
103 Boolean flipClock = FALSE;\r
104 extern HANDLE chatHandle[];\r
105 extern enum ICS_TYPE ics_type;\r
106 \r
107 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
108 int  MyGetFullPathName P((char *name, char *fullname));\r
109 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
110 VOID NewVariantPopup(HWND hwnd);\r
111 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
112                    /*char*/int promoChar));\r
113 void DisplayMove P((int moveNumber));\r
114 void ChatPopUp P((char *s));\r
115 typedef struct {\r
116   ChessSquare piece;  \r
117   POINT pos;      /* window coordinates of current pos */\r
118   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
119   POINT from;     /* board coordinates of the piece's orig pos */\r
120   POINT to;       /* board coordinates of the piece's new pos */\r
121 } AnimInfo;\r
122 \r
123 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
124 \r
125 typedef struct {\r
126   POINT start;    /* window coordinates of start pos */\r
127   POINT pos;      /* window coordinates of current pos */\r
128   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
129   POINT from;     /* board coordinates of the piece's orig pos */\r
130   ChessSquare piece;\r
131 } DragInfo;\r
132 \r
133 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
134 \r
135 typedef struct {\r
136   POINT sq[2];    /* board coordinates of from, to squares */\r
137 } HighlightInfo;\r
138 \r
139 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
143 \r
144 typedef struct { // [HGM] atomic\r
145   int fromX, fromY, toX, toY, radius;\r
146 } ExplodeInfo;\r
147 \r
148 static ExplodeInfo explodeInfo;\r
149 \r
150 /* Window class names */\r
151 char szAppName[] = "WinBoard";\r
152 char szConsoleName[] = "WBConsole";\r
153 \r
154 /* Title bar text */\r
155 char szTitle[] = "WinBoard";\r
156 char szConsoleTitle[] = "I C S Interaction";\r
157 \r
158 char *programName;\r
159 char *settingsFileName;\r
160 Boolean saveSettingsOnExit;\r
161 char installDir[MSG_SIZ];\r
162 int errorExitStatus;\r
163 \r
164 BoardSize boardSize;\r
165 Boolean chessProgram;\r
166 //static int boardX, boardY;\r
167 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
168 int squareSize, lineGap, minorSize, border;\r
169 static int winW, winH;\r
170 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
171 static int logoHeight = 0;\r
172 static char messageText[MESSAGE_TEXT_MAX];\r
173 static int clockTimerEvent = 0;\r
174 static int loadGameTimerEvent = 0;\r
175 static int analysisTimerEvent = 0;\r
176 static DelayedEventCallback delayedTimerCallback;\r
177 static int delayedTimerEvent = 0;\r
178 static int buttonCount = 2;\r
179 char *icsTextMenuString;\r
180 char *icsNames;\r
181 char *firstChessProgramNames;\r
182 char *secondChessProgramNames;\r
183 \r
184 #define PALETTESIZE 256\r
185 \r
186 HINSTANCE hInst;          /* current instance */\r
187 Boolean alwaysOnTop = FALSE;\r
188 RECT boardRect;\r
189 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
190   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
191 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
192 HPALETTE hPal;\r
193 ColorClass currentColorClass;\r
194 \r
195 static HWND savedHwnd;\r
196 HWND hCommPort = NULL;    /* currently open comm port */\r
197 static HWND hwndPause;    /* pause button */\r
198 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
199 static HBRUSH lightSquareBrush, darkSquareBrush,\r
200   blackSquareBrush, /* [HGM] for band between board and holdings */\r
201   explodeBrush,     /* [HGM] atomic */\r
202   markerBrush[8],   /* [HGM] markers */\r
203   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
204 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
205 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
206 static HPEN gridPen = NULL;\r
207 static HPEN highlightPen = NULL;\r
208 static HPEN premovePen = NULL;\r
209 static NPLOGPALETTE pLogPal;\r
210 static BOOL paletteChanged = FALSE;\r
211 static HICON iconWhite, iconBlack, iconCurrent;\r
212 static int doingSizing = FALSE;\r
213 static int lastSizing = 0;\r
214 static int prevStderrPort;\r
215 static HBITMAP userLogo;\r
216 \r
217 static HBITMAP liteBackTexture = NULL;\r
218 static HBITMAP darkBackTexture = NULL;\r
219 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
220 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int backTextureSquareSize = 0;\r
222 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
223 \r
224 #if __GNUC__ && !defined(_winmajor)\r
225 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
226 #else\r
227 \r
228 #if defined(_winmajor)\r
229 #define oldDialog (_winmajor < 4)\r
230 #else\r
231 #define oldDialog 0\r
232 #endif\r
233 #endif\r
234 \r
235 #define INTERNATIONAL\r
236 \r
237 #ifdef INTERNATIONAL\r
238 #  define _(s) T_(s)\r
239 #  define N_(s) s\r
240 #else\r
241 #  define _(s) s\r
242 #  define N_(s) s\r
243 #  define T_(s) s\r
244 #  define Translate(x, y)\r
245 #  define LoadLanguageFile(s)\r
246 #endif\r
247 \r
248 #ifdef INTERNATIONAL\r
249 \r
250 Boolean barbaric; // flag indicating if translation is needed\r
251 \r
252 // list of item numbers used in each dialog (used to alter language at run time)\r
253 \r
254 #define ABOUTBOX -1  /* not sure why these are needed */\r
255 #define ABOUTBOX2 -1\r
256 \r
257 int dialogItems[][42] = {\r
258 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
259 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
260   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
261 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
262   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
263 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
264   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
265 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
266 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
267   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
268 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
269 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
270   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
271 { ABOUTBOX2, IDC_ChessBoard }, \r
272 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
273   OPT_GameListClose, IDC_GameListDoFilter }, \r
274 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
275 { DLG_Error, IDOK }, \r
276 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
277   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
278 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
279 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
280   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
281   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
282 { DLG_IndexNumber, IDC_Index }, \r
283 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
284 { DLG_TypeInName, IDOK, IDCANCEL }, \r
285 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
286   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
287 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
288   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
289   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
290   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
291   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
292   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
293   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
294 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
295   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
296   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
297   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
298   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
299   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
300   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
301   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
302   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
303 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
304   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
305   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
306   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
307   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
308   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
309   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
310   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
311 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
312   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
313   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
314   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
315   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
316   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
317   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
318   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
319   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
320 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
321   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
322   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
323   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
324   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
325 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
326 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
327   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
328 { DLG_MoveHistory }, \r
329 { DLG_EvalGraph }, \r
330 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
331 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
332 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
333   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
334   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
335   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
336 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
337   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
338   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
339 { 0 }\r
340 };\r
341 \r
342 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
343 static int lastChecked;\r
344 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
345 extern int tinyLayout;\r
346 extern char * menuBarText[][10];\r
347 \r
348 void\r
349 LoadLanguageFile(char *name)\r
350 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
351     FILE *f;\r
352     int i=0, j=0, n=0, k;\r
353     char buf[MSG_SIZ];\r
354 \r
355     if(!name || name[0] == NULLCHAR) return;\r
356       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
357     appData.language = oldLanguage;\r
358     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
359     if((f = fopen(buf, "r")) == NULL) return;\r
360     while((k = fgetc(f)) != EOF) {\r
361         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
362         languageBuf[i] = k;\r
363         if(k == '\n') {\r
364             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
365                 char *p;\r
366                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
367                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
368                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
369                         english[j] = languageBuf + n + 1; *p = 0;\r
370                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
371 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
372                     }\r
373                 }\r
374             }\r
375             n = i + 1;\r
376         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
377             switch(k) {\r
378               case 'n': k = '\n'; break;\r
379               case 'r': k = '\r'; break;\r
380               case 't': k = '\t'; break;\r
381             }\r
382             languageBuf[--i] = k;\r
383         }\r
384         i++;\r
385     }\r
386     fclose(f);\r
387     barbaric = (j != 0);\r
388     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
389 }\r
390 \r
391 char *\r
392 T_(char *s)\r
393 {   // return the translation of the given string\r
394     // efficiency can be improved a lot...\r
395     int i=0;\r
396     static char buf[MSG_SIZ];\r
397 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
398     if(!barbaric) return s;\r
399     if(!s) return ""; // sanity\r
400     while(english[i]) {\r
401         if(!strcmp(s, english[i])) return foreign[i];\r
402         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
403             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
404             return buf;\r
405         }\r
406         i++;\r
407     }\r
408     return s;\r
409 }\r
410 \r
411 void\r
412 Translate(HWND hDlg, int dialogID)\r
413 {   // translate all text items in the given dialog\r
414     int i=0, j, k;\r
415     char buf[MSG_SIZ], *s;\r
416     if(!barbaric) return;\r
417     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
418     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
419     GetWindowText( hDlg, buf, MSG_SIZ );\r
420     s = T_(buf);\r
421     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
422     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
423         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
424         if(strlen(buf) == 0) continue;\r
425         s = T_(buf);\r
426         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
427     }\r
428 }\r
429 \r
430 HMENU\r
431 TranslateOneMenu(int i, HMENU subMenu)\r
432 {\r
433     int j;\r
434     static MENUITEMINFO info;\r
435 \r
436     info.cbSize = sizeof(MENUITEMINFO);\r
437     info.fMask = MIIM_STATE | MIIM_TYPE;\r
438           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
439             char buf[MSG_SIZ];\r
440             info.dwTypeData = buf;\r
441             info.cch = sizeof(buf);\r
442             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
443             if(i < 10) {\r
444                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
445                 else menuText[i][j] = strdup(buf); // remember original on first change\r
446             }\r
447             if(buf[0] == NULLCHAR) continue;\r
448             info.dwTypeData = T_(buf);\r
449             info.cch = strlen(buf)+1;\r
450             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
451           }\r
452     return subMenu;\r
453 }\r
454 \r
455 void\r
456 TranslateMenus(int addLanguage)\r
457 {\r
458     int i;\r
459     WIN32_FIND_DATA fileData;\r
460     HANDLE hFind;\r
461 #define IDM_English 1970\r
462     if(1) {\r
463         HMENU mainMenu = GetMenu(hwndMain);\r
464         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
465           HMENU subMenu = GetSubMenu(mainMenu, i);\r
466           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
467                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
468           TranslateOneMenu(i, subMenu);\r
469         }\r
470         DrawMenuBar(hwndMain);\r
471     }\r
472 \r
473     if(!addLanguage) return;\r
474     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
475         HMENU mainMenu = GetMenu(hwndMain);\r
476         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
477         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
478         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
479         i = 0; lastChecked = IDM_English;\r
480         do {\r
481             char *p, *q = fileData.cFileName;\r
482             int checkFlag = MF_UNCHECKED;\r
483             languageFile[i] = strdup(q);\r
484             if(barbaric && !strcmp(oldLanguage, q)) {\r
485                 checkFlag = MF_CHECKED;\r
486                 lastChecked = IDM_English + i + 1;\r
487                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
488             }\r
489             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
490             p = strstr(fileData.cFileName, ".lng");\r
491             if(p) *p = 0;\r
492             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
493         } while(FindNextFile(hFind, &fileData));\r
494         FindClose(hFind);\r
495     }\r
496 }\r
497 \r
498 #endif\r
499 \r
500 #define IDM_RecentEngines 3000\r
501 \r
502 void\r
503 RecentEngineMenu (char *s)\r
504 {\r
505     if(appData.icsActive) return;\r
506     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
507         HMENU mainMenu = GetMenu(hwndMain);\r
508         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
509         int i=IDM_RecentEngines;\r
510         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
511         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
512         while(*s) {\r
513           char *p = strchr(s, '\n');\r
514           if(p == NULL) return; // malformed!\r
515           *p = NULLCHAR;\r
516           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
517           *p = '\n';\r
518           s = p+1;\r
519         }\r
520     }\r
521 }\r
522 \r
523 \r
524 typedef struct {\r
525   char *name;\r
526   int squareSize;\r
527   int lineGap;\r
528   int smallLayout;\r
529   int tinyLayout;\r
530   int cliWidth, cliHeight;\r
531 } SizeInfo;\r
532 \r
533 SizeInfo sizeInfo[] = \r
534 {\r
535   { "tiny",     21, 0, 1, 1, 0, 0 },\r
536   { "teeny",    25, 1, 1, 1, 0, 0 },\r
537   { "dinky",    29, 1, 1, 1, 0, 0 },\r
538   { "petite",   33, 1, 1, 1, 0, 0 },\r
539   { "slim",     37, 2, 1, 0, 0, 0 },\r
540   { "small",    40, 2, 1, 0, 0, 0 },\r
541   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
542   { "middling", 49, 2, 0, 0, 0, 0 },\r
543   { "average",  54, 2, 0, 0, 0, 0 },\r
544   { "moderate", 58, 3, 0, 0, 0, 0 },\r
545   { "medium",   64, 3, 0, 0, 0, 0 },\r
546   { "bulky",    72, 3, 0, 0, 0, 0 },\r
547   { "large",    80, 3, 0, 0, 0, 0 },\r
548   { "big",      87, 3, 0, 0, 0, 0 },\r
549   { "huge",     95, 3, 0, 0, 0, 0 },\r
550   { "giant",    108, 3, 0, 0, 0, 0 },\r
551   { "colossal", 116, 4, 0, 0, 0, 0 },\r
552   { "titanic",  129, 4, 0, 0, 0, 0 },\r
553   { NULL, 0, 0, 0, 0, 0, 0 }\r
554 };\r
555 \r
556 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
557 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
558 {\r
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576   { 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
577 };\r
578 \r
579 MyFont *font[NUM_SIZES][NUM_FONTS];\r
580 \r
581 typedef struct {\r
582   char *label;\r
583   int id;\r
584   HWND hwnd;\r
585   WNDPROC wndproc;\r
586 } MyButtonDesc;\r
587 \r
588 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
589 #define N_BUTTONS 5\r
590 \r
591 MyButtonDesc buttonDesc[N_BUTTONS] =\r
592 {\r
593   {"<<", IDM_ToStart, NULL, NULL},\r
594   {"<", IDM_Backward, NULL, NULL},\r
595   {"P", IDM_Pause, NULL, NULL},\r
596   {">", IDM_Forward, NULL, NULL},\r
597   {">>", IDM_ToEnd, NULL, NULL},\r
598 };\r
599 \r
600 int tinyLayout = 0, smallLayout = 0;\r
601 #define MENU_BAR_ITEMS 9\r
602 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
603   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
604   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
605 };\r
606 \r
607 \r
608 MySound sounds[(int)NSoundClasses];\r
609 MyTextAttribs textAttribs[(int)NColorClasses];\r
610 \r
611 MyColorizeAttribs colorizeAttribs[] = {\r
612   { (COLORREF)0, 0, N_("Shout Text") },\r
613   { (COLORREF)0, 0, N_("SShout/CShout") },\r
614   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
615   { (COLORREF)0, 0, N_("Channel Text") },\r
616   { (COLORREF)0, 0, N_("Kibitz Text") },\r
617   { (COLORREF)0, 0, N_("Tell Text") },\r
618   { (COLORREF)0, 0, N_("Challenge Text") },\r
619   { (COLORREF)0, 0, N_("Request Text") },\r
620   { (COLORREF)0, 0, N_("Seek Text") },\r
621   { (COLORREF)0, 0, N_("Normal Text") },\r
622   { (COLORREF)0, 0, N_("None") }\r
623 };\r
624 \r
625 \r
626 \r
627 static char *commentTitle;\r
628 static char *commentText;\r
629 static int commentIndex;\r
630 static Boolean editComment = FALSE;\r
631 \r
632 \r
633 char errorTitle[MSG_SIZ];\r
634 char errorMessage[2*MSG_SIZ];\r
635 HWND errorDialog = NULL;\r
636 BOOLEAN moveErrorMessageUp = FALSE;\r
637 BOOLEAN consoleEcho = TRUE;\r
638 CHARFORMAT consoleCF;\r
639 COLORREF consoleBackgroundColor;\r
640 \r
641 char *programVersion;\r
642 \r
643 #define CPReal 1\r
644 #define CPComm 2\r
645 #define CPSock 3\r
646 #define CPRcmd 4\r
647 typedef int CPKind;\r
648 \r
649 typedef struct {\r
650   CPKind kind;\r
651   HANDLE hProcess;\r
652   DWORD pid;\r
653   HANDLE hTo;\r
654   HANDLE hFrom;\r
655   SOCKET sock;\r
656   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
657 } ChildProc;\r
658 \r
659 #define INPUT_SOURCE_BUF_SIZE 4096\r
660 \r
661 typedef struct _InputSource {\r
662   CPKind kind;\r
663   HANDLE hFile;\r
664   SOCKET sock;\r
665   int lineByLine;\r
666   HANDLE hThread;\r
667   DWORD id;\r
668   char buf[INPUT_SOURCE_BUF_SIZE];\r
669   char *next;\r
670   DWORD count;\r
671   int error;\r
672   InputCallback func;\r
673   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
674   VOIDSTAR closure;\r
675 } InputSource;\r
676 \r
677 InputSource *consoleInputSource;\r
678 \r
679 DCB dcb;\r
680 \r
681 /* forward */\r
682 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
683 VOID ConsoleCreate();\r
684 LRESULT CALLBACK\r
685   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
686 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
687 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
688 VOID ParseCommSettings(char *arg, DCB *dcb);\r
689 LRESULT CALLBACK\r
690   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
691 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
692 void ParseIcsTextMenu(char *icsTextMenuString);\r
693 VOID PopUpNameDialog(char firstchar);\r
694 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
695 \r
696 /* [AS] */\r
697 int NewGameFRC();\r
698 int GameListOptions();\r
699 \r
700 int dummy; // [HGM] for obsolete args\r
701 \r
702 HWND hwndMain = NULL;        /* root window*/\r
703 HWND hwndConsole = NULL;\r
704 HWND commentDialog = NULL;\r
705 HWND moveHistoryDialog = NULL;\r
706 HWND evalGraphDialog = NULL;\r
707 HWND engineOutputDialog = NULL;\r
708 HWND gameListDialog = NULL;\r
709 HWND editTagsDialog = NULL;\r
710 \r
711 int commentUp = FALSE;\r
712 \r
713 WindowPlacement wpMain;\r
714 WindowPlacement wpConsole;\r
715 WindowPlacement wpComment;\r
716 WindowPlacement wpMoveHistory;\r
717 WindowPlacement wpEvalGraph;\r
718 WindowPlacement wpEngineOutput;\r
719 WindowPlacement wpGameList;\r
720 WindowPlacement wpTags;\r
721 \r
722 VOID EngineOptionsPopup(); // [HGM] settings\r
723 \r
724 VOID GothicPopUp(char *title, VariantClass variant);\r
725 /*\r
726  * Setting "frozen" should disable all user input other than deleting\r
727  * the window.  We do this while engines are initializing themselves.\r
728  */\r
729 static int frozen = 0;\r
730 static int oldMenuItemState[MENU_BAR_ITEMS];\r
731 void FreezeUI()\r
732 {\r
733   HMENU hmenu;\r
734   int i;\r
735 \r
736   if (frozen) return;\r
737   frozen = 1;\r
738   hmenu = GetMenu(hwndMain);\r
739   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
740     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
741   }\r
742   DrawMenuBar(hwndMain);\r
743 }\r
744 \r
745 /* Undo a FreezeUI */\r
746 void ThawUI()\r
747 {\r
748   HMENU hmenu;\r
749   int i;\r
750 \r
751   if (!frozen) return;\r
752   frozen = 0;\r
753   hmenu = GetMenu(hwndMain);\r
754   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
755     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
756   }\r
757   DrawMenuBar(hwndMain);\r
758 }\r
759 \r
760 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
761 \r
762 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
763 #ifdef JAWS\r
764 #include "jaws.c"\r
765 #else\r
766 #define JAWS_INIT\r
767 #define JAWS_ARGS\r
768 #define JAWS_ALT_INTERCEPT\r
769 #define JAWS_KBUP_NAVIGATION\r
770 #define JAWS_KBDOWN_NAVIGATION\r
771 #define JAWS_MENU_ITEMS\r
772 #define JAWS_SILENCE\r
773 #define JAWS_REPLAY\r
774 #define JAWS_ACCEL\r
775 #define JAWS_COPYRIGHT\r
776 #define JAWS_DELETE(X) X\r
777 #define SAYMACHINEMOVE()\r
778 #define SAY(X)\r
779 #endif\r
780 \r
781 /*---------------------------------------------------------------------------*\\r
782  *\r
783  * WinMain\r
784  *\r
785 \*---------------------------------------------------------------------------*/\r
786 \r
787 int APIENTRY\r
788 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
789         LPSTR lpCmdLine, int nCmdShow)\r
790 {\r
791   MSG msg;\r
792   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
793 //  INITCOMMONCONTROLSEX ex;\r
794 \r
795   debugFP = stderr;\r
796 \r
797   LoadLibrary("RICHED32.DLL");\r
798   consoleCF.cbSize = sizeof(CHARFORMAT);\r
799 \r
800   if (!InitApplication(hInstance)) {\r
801     return (FALSE);\r
802   }\r
803   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
804     return (FALSE);\r
805   }\r
806 \r
807   JAWS_INIT\r
808   TranslateMenus(1);\r
809 \r
810 //  InitCommonControlsEx(&ex);\r
811   InitCommonControls();\r
812 \r
813   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
814   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
815   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
816 \r
817   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
818 \r
819   while (GetMessage(&msg, /* message structure */\r
820                     NULL, /* handle of window receiving the message */\r
821                     0,    /* lowest message to examine */\r
822                     0))   /* highest message to examine */\r
823     {\r
824 \r
825       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
826         // [HGM] navigate: switch between all windows with tab\r
827         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
828         int i, currentElement = 0;\r
829 \r
830         // first determine what element of the chain we come from (if any)\r
831         if(appData.icsActive) {\r
832             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
833             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
834         }\r
835         if(engineOutputDialog && EngineOutputIsUp()) {\r
836             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
837             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
838         }\r
839         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
840             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
841         }\r
842         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
843         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
844         if(msg.hwnd == e1)                 currentElement = 2; else\r
845         if(msg.hwnd == e2)                 currentElement = 3; else\r
846         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
847         if(msg.hwnd == mh)                currentElement = 4; else\r
848         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
849         if(msg.hwnd == hText)  currentElement = 5; else\r
850         if(msg.hwnd == hInput) currentElement = 6; else\r
851         for (i = 0; i < N_BUTTONS; i++) {\r
852             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
853         }\r
854 \r
855         // determine where to go to\r
856         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
857           do {\r
858             currentElement = (currentElement + direction) % 7;\r
859             switch(currentElement) {\r
860                 case 0:\r
861                   h = hwndMain; break; // passing this case always makes the loop exit\r
862                 case 1:\r
863                   h = buttonDesc[0].hwnd; break; // could be NULL\r
864                 case 2:\r
865                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
866                   h = e1; break;\r
867                 case 3:\r
868                   if(!EngineOutputIsUp()) continue;\r
869                   h = e2; break;\r
870                 case 4:\r
871                   if(!MoveHistoryIsUp()) continue;\r
872                   h = mh; break;\r
873 //              case 6: // input to eval graph does not seem to get here!\r
874 //                if(!EvalGraphIsUp()) continue;\r
875 //                h = evalGraphDialog; break;\r
876                 case 5:\r
877                   if(!appData.icsActive) continue;\r
878                   SAY("display");\r
879                   h = hText; break;\r
880                 case 6:\r
881                   if(!appData.icsActive) continue;\r
882                   SAY("input");\r
883                   h = hInput; break;\r
884             }\r
885           } while(h == 0);\r
886 \r
887           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
888           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
889           SetFocus(h);\r
890 \r
891           continue; // this message now has been processed\r
892         }\r
893       }\r
894 \r
895       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
896           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
897           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
898           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
899           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
900           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
901           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
902           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
903           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
904           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
905         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
906         for(i=0; i<MAX_CHAT; i++) \r
907             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
908                 done = 1; break;\r
909         }\r
910         if(done) continue; // [HGM] chat: end patch\r
911         TranslateMessage(&msg); /* Translates virtual key codes */\r
912         DispatchMessage(&msg);  /* Dispatches message to window */\r
913       }\r
914     }\r
915 \r
916 \r
917   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
918 }\r
919 \r
920 /*---------------------------------------------------------------------------*\\r
921  *\r
922  * Initialization functions\r
923  *\r
924 \*---------------------------------------------------------------------------*/\r
925 \r
926 void\r
927 SetUserLogo()\r
928 {   // update user logo if necessary\r
929     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
930 \r
931     if(appData.autoLogo) {\r
932           curName = UserName();\r
933           if(strcmp(curName, oldUserName)) {\r
934                 GetCurrentDirectory(MSG_SIZ, dir);\r
935                 SetCurrentDirectory(installDir);\r
936                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
937                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
938                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
939                 if(userLogo == NULL)\r
940                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
941                 SetCurrentDirectory(dir); /* return to prev directory */\r
942           }\r
943     }\r
944 }\r
945 \r
946 BOOL\r
947 InitApplication(HINSTANCE hInstance)\r
948 {\r
949   WNDCLASS wc;\r
950 \r
951   /* Fill in window class structure with parameters that describe the */\r
952   /* main window. */\r
953 \r
954   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
955   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
956   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
957   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
958   wc.hInstance     = hInstance;         /* Owner of this class */\r
959   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
960   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
961   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
962   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
963   wc.lpszClassName = szAppName;                 /* Name to register as */\r
964 \r
965   /* Register the window class and return success/failure code. */\r
966   if (!RegisterClass(&wc)) return FALSE;\r
967 \r
968   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
969   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
970   wc.cbClsExtra    = 0;\r
971   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
972   wc.hInstance     = hInstance;\r
973   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
974   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
975   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
976   wc.lpszMenuName  = NULL;\r
977   wc.lpszClassName = szConsoleName;\r
978 \r
979   if (!RegisterClass(&wc)) return FALSE;\r
980   return TRUE;\r
981 }\r
982 \r
983 \r
984 /* Set by InitInstance, used by EnsureOnScreen */\r
985 int screenHeight, screenWidth;\r
986 \r
987 void\r
988 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
989 {\r
990 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
991   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
992   if (*x > screenWidth - 32) *x = 0;\r
993   if (*y > screenHeight - 32) *y = 0;\r
994   if (*x < minX) *x = minX;\r
995   if (*y < minY) *y = minY;\r
996 }\r
997 \r
998 VOID\r
999 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1000 {\r
1001   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1002   GetCurrentDirectory(MSG_SIZ, dir);\r
1003   SetCurrentDirectory(installDir);\r
1004   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1005       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1006 \r
1007       if (cps->programLogo == NULL && appData.debugMode) {\r
1008           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1009       }\r
1010   } else if(appData.autoLogo) {\r
1011       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1012         char *opponent = "";\r
1013         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1014         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1015         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1016         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1017             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1018             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1019         }\r
1020       } else\r
1021       if(appData.directory[n] && appData.directory[n][0]) {\r
1022         SetCurrentDirectory(appData.directory[n]);\r
1023         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1024       }\r
1025   }\r
1026   SetCurrentDirectory(dir); /* return to prev directory */\r
1027 }\r
1028 \r
1029 VOID\r
1030 InitTextures()\r
1031 {\r
1032   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1033   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1034   \r
1035   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1036       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1037       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1038       liteBackTextureMode = appData.liteBackTextureMode;\r
1039 \r
1040       if (liteBackTexture == NULL && appData.debugMode) {\r
1041           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1042       }\r
1043   }\r
1044   \r
1045   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1046       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1047       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1048       darkBackTextureMode = appData.darkBackTextureMode;\r
1049 \r
1050       if (darkBackTexture == NULL && appData.debugMode) {\r
1051           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1052       }\r
1053   }\r
1054 }\r
1055 \r
1056 BOOL\r
1057 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1058 {\r
1059   HWND hwnd; /* Main window handle. */\r
1060   int ibs;\r
1061   WINDOWPLACEMENT wp;\r
1062   char *filepart;\r
1063 \r
1064   hInst = hInstance;    /* Store instance handle in our global variable */\r
1065   programName = szAppName;\r
1066 \r
1067   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1068     *filepart = NULLCHAR;\r
1069     SetCurrentDirectory(installDir);\r
1070   } else {\r
1071     GetCurrentDirectory(MSG_SIZ, installDir);\r
1072   }\r
1073   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1074   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1075   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1076   /* xboard, and older WinBoards, controlled the move sound with the\r
1077      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1078      always turn the option on (so that the backend will call us),\r
1079      then let the user turn the sound off by setting it to silence if\r
1080      desired.  To accommodate old winboard.ini files saved by old\r
1081      versions of WinBoard, we also turn off the sound if the option\r
1082      was initially set to false. [HGM] taken out of InitAppData */\r
1083   if (!appData.ringBellAfterMoves) {\r
1084     sounds[(int)SoundMove].name = strdup("");\r
1085     appData.ringBellAfterMoves = TRUE;\r
1086   }\r
1087   if (appData.debugMode) {\r
1088     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1089     setbuf(debugFP, NULL);\r
1090   }\r
1091 \r
1092   LoadLanguageFile(appData.language);\r
1093 \r
1094   InitBackEnd1();\r
1095 \r
1096 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1097 //  InitEngineUCI( installDir, &second );\r
1098 \r
1099   /* Create a main window for this application instance. */\r
1100   hwnd = CreateWindow(szAppName, szTitle,\r
1101                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1102                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1103                       NULL, NULL, hInstance, NULL);\r
1104   hwndMain = hwnd;\r
1105 \r
1106   /* If window could not be created, return "failure" */\r
1107   if (!hwnd) {\r
1108     return (FALSE);\r
1109   }\r
1110 \r
1111   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1112   LoadLogo(&first, 0, FALSE);\r
1113   LoadLogo(&second, 1, appData.icsActive);\r
1114 \r
1115   SetUserLogo();\r
1116 \r
1117   iconWhite = LoadIcon(hInstance, "icon_white");\r
1118   iconBlack = LoadIcon(hInstance, "icon_black");\r
1119   iconCurrent = iconWhite;\r
1120   InitDrawingColors();\r
1121   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1122   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1123   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1124   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1125     /* Compute window size for each board size, and use the largest\r
1126        size that fits on this screen as the default. */\r
1127     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1128     if (boardSize == (BoardSize)-1 &&\r
1129         winH <= screenHeight\r
1130            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1131         && winW <= screenWidth) {\r
1132       boardSize = (BoardSize)ibs;\r
1133     }\r
1134   }\r
1135 \r
1136   InitDrawingSizes(boardSize, 0);\r
1137   RecentEngineMenu(appData.recentEngineList);\r
1138   InitMenuChecks();\r
1139   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1140 \r
1141   /* [AS] Load textures if specified */\r
1142   InitTextures();\r
1143 \r
1144   mysrandom( (unsigned) time(NULL) );\r
1145 \r
1146   /* [AS] Restore layout */\r
1147   if( wpMoveHistory.visible ) {\r
1148       MoveHistoryPopUp();\r
1149   }\r
1150 \r
1151   if( wpEvalGraph.visible ) {\r
1152       EvalGraphPopUp();\r
1153   }\r
1154 \r
1155   if( wpEngineOutput.visible ) {\r
1156       EngineOutputPopUp();\r
1157   }\r
1158 \r
1159   /* Make the window visible; update its client area; and return "success" */\r
1160   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1161   wp.length = sizeof(WINDOWPLACEMENT);\r
1162   wp.flags = 0;\r
1163   wp.showCmd = nCmdShow;\r
1164   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1165   wp.rcNormalPosition.left = wpMain.x;\r
1166   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1167   wp.rcNormalPosition.top = wpMain.y;\r
1168   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1169   SetWindowPlacement(hwndMain, &wp);\r
1170 \r
1171   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1172 \r
1173   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1174                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1175 \r
1176   if (hwndConsole) {\r
1177 #if AOT_CONSOLE\r
1178     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1179                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1180 #endif\r
1181     ShowWindow(hwndConsole, nCmdShow);\r
1182     SetActiveWindow(hwndConsole);\r
1183   }\r
1184   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1185   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1186 \r
1187   return TRUE;\r
1188 \r
1189 }\r
1190 \r
1191 VOID\r
1192 InitMenuChecks()\r
1193 {\r
1194   HMENU hmenu = GetMenu(hwndMain);\r
1195 \r
1196   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1197                         MF_BYCOMMAND|((appData.icsActive &&\r
1198                                        *appData.icsCommPort != NULLCHAR) ?\r
1199                                       MF_ENABLED : MF_GRAYED));\r
1200   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1201                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1202                                      MF_CHECKED : MF_UNCHECKED));\r
1203 }\r
1204 \r
1205 //---------------------------------------------------------------------------------------------------------\r
1206 \r
1207 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1208 #define XBOARD FALSE\r
1209 \r
1210 #define OPTCHAR "/"\r
1211 #define SEPCHAR "="\r
1212 #define TOPLEVEL 0\r
1213 \r
1214 #include "args.h"\r
1215 \r
1216 // front-end part of option handling\r
1217 \r
1218 VOID\r
1219 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1220 {\r
1221   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1222   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1223   DeleteDC(hdc);\r
1224   lf->lfWidth = 0;\r
1225   lf->lfEscapement = 0;\r
1226   lf->lfOrientation = 0;\r
1227   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1228   lf->lfItalic = mfp->italic;\r
1229   lf->lfUnderline = mfp->underline;\r
1230   lf->lfStrikeOut = mfp->strikeout;\r
1231   lf->lfCharSet = mfp->charset;\r
1232   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1233   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1234   lf->lfQuality = DEFAULT_QUALITY;\r
1235   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1236     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1237 }\r
1238 \r
1239 void\r
1240 CreateFontInMF(MyFont *mf)\r
1241\r
1242   LFfromMFP(&mf->lf, &mf->mfp);\r
1243   if (mf->hf) DeleteObject(mf->hf);\r
1244   mf->hf = CreateFontIndirect(&mf->lf);\r
1245 }\r
1246 \r
1247 // [HGM] This platform-dependent table provides the location for storing the color info\r
1248 void *\r
1249 colorVariable[] = {\r
1250   &whitePieceColor, \r
1251   &blackPieceColor, \r
1252   &lightSquareColor,\r
1253   &darkSquareColor, \r
1254   &highlightSquareColor,\r
1255   &premoveHighlightColor,\r
1256   NULL,\r
1257   &consoleBackgroundColor,\r
1258   &appData.fontForeColorWhite,\r
1259   &appData.fontBackColorWhite,\r
1260   &appData.fontForeColorBlack,\r
1261   &appData.fontBackColorBlack,\r
1262   &appData.evalHistColorWhite,\r
1263   &appData.evalHistColorBlack,\r
1264   &appData.highlightArrowColor,\r
1265 };\r
1266 \r
1267 /* Command line font name parser.  NULL name means do nothing.\r
1268    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1269    For backward compatibility, syntax without the colon is also\r
1270    accepted, but font names with digits in them won't work in that case.\r
1271 */\r
1272 VOID\r
1273 ParseFontName(char *name, MyFontParams *mfp)\r
1274 {\r
1275   char *p, *q;\r
1276   if (name == NULL) return;\r
1277   p = name;\r
1278   q = strchr(p, ':');\r
1279   if (q) {\r
1280     if (q - p >= sizeof(mfp->faceName))\r
1281       ExitArgError(_("Font name too long:"), name, TRUE);\r
1282     memcpy(mfp->faceName, p, q - p);\r
1283     mfp->faceName[q - p] = NULLCHAR;\r
1284     p = q + 1;\r
1285   } else {\r
1286     q = mfp->faceName;\r
1287 \r
1288     while (*p && !isdigit(*p)) {\r
1289       *q++ = *p++;\r
1290       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1291         ExitArgError(_("Font name too long:"), name, TRUE);\r
1292     }\r
1293     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1294     *q = NULLCHAR;\r
1295   }\r
1296   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1297   mfp->pointSize = (float) atof(p);\r
1298   mfp->bold = (strchr(p, 'b') != NULL);\r
1299   mfp->italic = (strchr(p, 'i') != NULL);\r
1300   mfp->underline = (strchr(p, 'u') != NULL);\r
1301   mfp->strikeout = (strchr(p, 's') != NULL);\r
1302   mfp->charset = DEFAULT_CHARSET;\r
1303   q = strchr(p, 'c');\r
1304   if (q)\r
1305     mfp->charset = (BYTE) atoi(q+1);\r
1306 }\r
1307 \r
1308 void\r
1309 ParseFont(char *name, int number)\r
1310 { // wrapper to shield back-end from 'font'\r
1311   ParseFontName(name, &font[boardSize][number]->mfp);\r
1312 }\r
1313 \r
1314 void\r
1315 SetFontDefaults()\r
1316 { // in WB  we have a 2D array of fonts; this initializes their description\r
1317   int i, j;\r
1318   /* Point font array elements to structures and\r
1319      parse default font names */\r
1320   for (i=0; i<NUM_FONTS; i++) {\r
1321     for (j=0; j<NUM_SIZES; j++) {\r
1322       font[j][i] = &fontRec[j][i];\r
1323       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1324     }\r
1325   }\r
1326 }\r
1327 \r
1328 void\r
1329 CreateFonts()\r
1330 { // here we create the actual fonts from the selected descriptions\r
1331   int i, j;\r
1332   for (i=0; i<NUM_FONTS; i++) {\r
1333     for (j=0; j<NUM_SIZES; j++) {\r
1334       CreateFontInMF(font[j][i]);\r
1335     }\r
1336   }\r
1337 }\r
1338 /* Color name parser.\r
1339    X version accepts X color names, but this one\r
1340    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1341 COLORREF\r
1342 ParseColorName(char *name)\r
1343 {\r
1344   int red, green, blue, count;\r
1345   char buf[MSG_SIZ];\r
1346 \r
1347   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1348   if (count != 3) {\r
1349     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1350       &red, &green, &blue);\r
1351   }\r
1352   if (count != 3) {\r
1353     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1354     DisplayError(buf, 0);\r
1355     return RGB(0, 0, 0);\r
1356   }\r
1357   return PALETTERGB(red, green, blue);\r
1358 }\r
1359 \r
1360 void\r
1361 ParseColor(int n, char *name)\r
1362 { // for WinBoard the color is an int, which needs to be derived from the string\r
1363   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1364 }\r
1365 \r
1366 void\r
1367 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1368 {\r
1369   char *e = argValue;\r
1370   int eff = 0;\r
1371 \r
1372   while (*e) {\r
1373     if (*e == 'b')      eff |= CFE_BOLD;\r
1374     else if (*e == 'i') eff |= CFE_ITALIC;\r
1375     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1376     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1377     else if (*e == '#' || isdigit(*e)) break;\r
1378     e++;\r
1379   }\r
1380   *effects = eff;\r
1381   *color   = ParseColorName(e);\r
1382 }\r
1383 \r
1384 void\r
1385 ParseTextAttribs(ColorClass cc, char *s)\r
1386 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1387     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1388     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1389 }\r
1390 \r
1391 void\r
1392 ParseBoardSize(void *addr, char *name)\r
1393 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1394   BoardSize bs = SizeTiny;\r
1395   while (sizeInfo[bs].name != NULL) {\r
1396     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1397         *(BoardSize *)addr = bs;\r
1398         return;\r
1399     }\r
1400     bs++;\r
1401   }\r
1402   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1403 }\r
1404 \r
1405 void\r
1406 LoadAllSounds()\r
1407 { // [HGM] import name from appData first\r
1408   ColorClass cc;\r
1409   SoundClass sc;\r
1410   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1411     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1412     textAttribs[cc].sound.data = NULL;\r
1413     MyLoadSound(&textAttribs[cc].sound);\r
1414   }\r
1415   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1416     textAttribs[cc].sound.name = strdup("");\r
1417     textAttribs[cc].sound.data = NULL;\r
1418   }\r
1419   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1420     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1421     sounds[sc].data = NULL;\r
1422     MyLoadSound(&sounds[sc]);\r
1423   }\r
1424 }\r
1425 \r
1426 void\r
1427 SetCommPortDefaults()\r
1428 {\r
1429    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1430   dcb.DCBlength = sizeof(DCB);\r
1431   dcb.BaudRate = 9600;\r
1432   dcb.fBinary = TRUE;\r
1433   dcb.fParity = FALSE;\r
1434   dcb.fOutxCtsFlow = FALSE;\r
1435   dcb.fOutxDsrFlow = FALSE;\r
1436   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1437   dcb.fDsrSensitivity = FALSE;\r
1438   dcb.fTXContinueOnXoff = TRUE;\r
1439   dcb.fOutX = FALSE;\r
1440   dcb.fInX = FALSE;\r
1441   dcb.fNull = FALSE;\r
1442   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1443   dcb.fAbortOnError = FALSE;\r
1444   dcb.ByteSize = 7;\r
1445   dcb.Parity = SPACEPARITY;\r
1446   dcb.StopBits = ONESTOPBIT;\r
1447 }\r
1448 \r
1449 // [HGM] args: these three cases taken out to stay in front-end\r
1450 void\r
1451 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1452 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1453         // while the curent board size determines the element. This system should be ported to XBoard.\r
1454         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1455         int bs;\r
1456         for (bs=0; bs<NUM_SIZES; bs++) {\r
1457           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1458           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1459           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1460             ad->argName, mfp->faceName, mfp->pointSize,\r
1461             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1462             mfp->bold ? "b" : "",\r
1463             mfp->italic ? "i" : "",\r
1464             mfp->underline ? "u" : "",\r
1465             mfp->strikeout ? "s" : "",\r
1466             (int)mfp->charset);\r
1467         }\r
1468       }\r
1469 \r
1470 void\r
1471 ExportSounds()\r
1472 { // [HGM] copy the names from the internal WB variables to appData\r
1473   ColorClass cc;\r
1474   SoundClass sc;\r
1475   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1476     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1477   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1478     (&appData.soundMove)[sc] = sounds[sc].name;\r
1479 }\r
1480 \r
1481 void\r
1482 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1483 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1484         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1485         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1486           (ta->effects & CFE_BOLD) ? "b" : "",\r
1487           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1488           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1489           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1490           (ta->effects) ? " " : "",\r
1491           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1492       }\r
1493 \r
1494 void\r
1495 SaveColor(FILE *f, ArgDescriptor *ad)\r
1496 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1497         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1498         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1499           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1500 }\r
1501 \r
1502 void\r
1503 SaveBoardSize(FILE *f, char *name, void *addr)\r
1504 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1505   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1506 }\r
1507 \r
1508 void\r
1509 ParseCommPortSettings(char *s)\r
1510 { // wrapper to keep dcb from back-end\r
1511   ParseCommSettings(s, &dcb);\r
1512 }\r
1513 \r
1514 void\r
1515 GetWindowCoords()\r
1516 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1517   GetActualPlacement(hwndMain, &wpMain);\r
1518   GetActualPlacement(hwndConsole, &wpConsole);\r
1519   GetActualPlacement(commentDialog, &wpComment);\r
1520   GetActualPlacement(editTagsDialog, &wpTags);\r
1521   GetActualPlacement(gameListDialog, &wpGameList);\r
1522   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1523   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1524   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1525 }\r
1526 \r
1527 void\r
1528 PrintCommPortSettings(FILE *f, char *name)\r
1529 { // wrapper to shield back-end from DCB\r
1530       PrintCommSettings(f, name, &dcb);\r
1531 }\r
1532 \r
1533 int\r
1534 MySearchPath(char *installDir, char *name, char *fullname)\r
1535 {\r
1536   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1537   if(name[0]== '%') {\r
1538     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1539     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1540       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1541       *strchr(buf, '%') = 0;\r
1542       strcat(fullname, getenv(buf));\r
1543       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1544     }\r
1545     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1546     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1547     return (int) strlen(fullname);\r
1548   }\r
1549   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1550 }\r
1551 \r
1552 int\r
1553 MyGetFullPathName(char *name, char *fullname)\r
1554 {\r
1555   char *dummy;\r
1556   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1557 }\r
1558 \r
1559 int\r
1560 MainWindowUp()\r
1561 { // [HGM] args: allows testing if main window is realized from back-end\r
1562   return hwndMain != NULL;\r
1563 }\r
1564 \r
1565 void\r
1566 PopUpStartupDialog()\r
1567 {\r
1568     FARPROC lpProc;\r
1569     \r
1570     LoadLanguageFile(appData.language);\r
1571     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1572     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1573     FreeProcInstance(lpProc);\r
1574 }\r
1575 \r
1576 /*---------------------------------------------------------------------------*\\r
1577  *\r
1578  * GDI board drawing routines\r
1579  *\r
1580 \*---------------------------------------------------------------------------*/\r
1581 \r
1582 /* [AS] Draw square using background texture */\r
1583 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1584 {\r
1585     XFORM   x;\r
1586 \r
1587     if( mode == 0 ) {\r
1588         return; /* Should never happen! */\r
1589     }\r
1590 \r
1591     SetGraphicsMode( dst, GM_ADVANCED );\r
1592 \r
1593     switch( mode ) {\r
1594     case 1:\r
1595         /* Identity */\r
1596         break;\r
1597     case 2:\r
1598         /* X reflection */\r
1599         x.eM11 = -1.0;\r
1600         x.eM12 = 0;\r
1601         x.eM21 = 0;\r
1602         x.eM22 = 1.0;\r
1603         x.eDx = (FLOAT) dw + dx - 1;\r
1604         x.eDy = 0;\r
1605         dx = 0;\r
1606         SetWorldTransform( dst, &x );\r
1607         break;\r
1608     case 3:\r
1609         /* Y reflection */\r
1610         x.eM11 = 1.0;\r
1611         x.eM12 = 0;\r
1612         x.eM21 = 0;\r
1613         x.eM22 = -1.0;\r
1614         x.eDx = 0;\r
1615         x.eDy = (FLOAT) dh + dy - 1;\r
1616         dy = 0;\r
1617         SetWorldTransform( dst, &x );\r
1618         break;\r
1619     case 4:\r
1620         /* X/Y flip */\r
1621         x.eM11 = 0;\r
1622         x.eM12 = 1.0;\r
1623         x.eM21 = 1.0;\r
1624         x.eM22 = 0;\r
1625         x.eDx = (FLOAT) dx;\r
1626         x.eDy = (FLOAT) dy;\r
1627         dx = 0;\r
1628         dy = 0;\r
1629         SetWorldTransform( dst, &x );\r
1630         break;\r
1631     }\r
1632 \r
1633     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1634 \r
1635     x.eM11 = 1.0;\r
1636     x.eM12 = 0;\r
1637     x.eM21 = 0;\r
1638     x.eM22 = 1.0;\r
1639     x.eDx = 0;\r
1640     x.eDy = 0;\r
1641     SetWorldTransform( dst, &x );\r
1642 \r
1643     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1644 }\r
1645 \r
1646 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1647 enum {\r
1648     PM_WP = (int) WhitePawn, \r
1649     PM_WN = (int) WhiteKnight, \r
1650     PM_WB = (int) WhiteBishop, \r
1651     PM_WR = (int) WhiteRook, \r
1652     PM_WQ = (int) WhiteQueen, \r
1653     PM_WF = (int) WhiteFerz, \r
1654     PM_WW = (int) WhiteWazir, \r
1655     PM_WE = (int) WhiteAlfil, \r
1656     PM_WM = (int) WhiteMan, \r
1657     PM_WO = (int) WhiteCannon, \r
1658     PM_WU = (int) WhiteUnicorn, \r
1659     PM_WH = (int) WhiteNightrider, \r
1660     PM_WA = (int) WhiteAngel, \r
1661     PM_WC = (int) WhiteMarshall, \r
1662     PM_WAB = (int) WhiteCardinal, \r
1663     PM_WD = (int) WhiteDragon, \r
1664     PM_WL = (int) WhiteLance, \r
1665     PM_WS = (int) WhiteCobra, \r
1666     PM_WV = (int) WhiteFalcon, \r
1667     PM_WSG = (int) WhiteSilver, \r
1668     PM_WG = (int) WhiteGrasshopper, \r
1669     PM_WK = (int) WhiteKing,\r
1670     PM_BP = (int) BlackPawn, \r
1671     PM_BN = (int) BlackKnight, \r
1672     PM_BB = (int) BlackBishop, \r
1673     PM_BR = (int) BlackRook, \r
1674     PM_BQ = (int) BlackQueen, \r
1675     PM_BF = (int) BlackFerz, \r
1676     PM_BW = (int) BlackWazir, \r
1677     PM_BE = (int) BlackAlfil, \r
1678     PM_BM = (int) BlackMan,\r
1679     PM_BO = (int) BlackCannon, \r
1680     PM_BU = (int) BlackUnicorn, \r
1681     PM_BH = (int) BlackNightrider, \r
1682     PM_BA = (int) BlackAngel, \r
1683     PM_BC = (int) BlackMarshall, \r
1684     PM_BG = (int) BlackGrasshopper, \r
1685     PM_BAB = (int) BlackCardinal,\r
1686     PM_BD = (int) BlackDragon,\r
1687     PM_BL = (int) BlackLance,\r
1688     PM_BS = (int) BlackCobra,\r
1689     PM_BV = (int) BlackFalcon,\r
1690     PM_BSG = (int) BlackSilver,\r
1691     PM_BK = (int) BlackKing\r
1692 };\r
1693 \r
1694 static HFONT hPieceFont = NULL;\r
1695 static HBITMAP hPieceMask[(int) EmptySquare];\r
1696 static HBITMAP hPieceFace[(int) EmptySquare];\r
1697 static int fontBitmapSquareSize = 0;\r
1698 static char pieceToFontChar[(int) EmptySquare] =\r
1699                               { 'p', 'n', 'b', 'r', 'q', \r
1700                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1701                       'k', 'o', 'm', 'v', 't', 'w', \r
1702                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1703                                                               'l' };\r
1704 \r
1705 extern BOOL SetCharTable( char *table, const char * map );\r
1706 /* [HGM] moved to backend.c */\r
1707 \r
1708 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1709 {\r
1710     HBRUSH hbrush;\r
1711     BYTE r1 = GetRValue( color );\r
1712     BYTE g1 = GetGValue( color );\r
1713     BYTE b1 = GetBValue( color );\r
1714     BYTE r2 = r1 / 2;\r
1715     BYTE g2 = g1 / 2;\r
1716     BYTE b2 = b1 / 2;\r
1717     RECT rc;\r
1718 \r
1719     /* Create a uniform background first */\r
1720     hbrush = CreateSolidBrush( color );\r
1721     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1722     FillRect( hdc, &rc, hbrush );\r
1723     DeleteObject( hbrush );\r
1724     \r
1725     if( mode == 1 ) {\r
1726         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1727         int steps = squareSize / 2;\r
1728         int i;\r
1729 \r
1730         for( i=0; i<steps; i++ ) {\r
1731             BYTE r = r1 - (r1-r2) * i / steps;\r
1732             BYTE g = g1 - (g1-g2) * i / steps;\r
1733             BYTE b = b1 - (b1-b2) * i / steps;\r
1734 \r
1735             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1736             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1737             FillRect( hdc, &rc, hbrush );\r
1738             DeleteObject(hbrush);\r
1739         }\r
1740     }\r
1741     else if( mode == 2 ) {\r
1742         /* Diagonal gradient, good more or less for every piece */\r
1743         POINT triangle[3];\r
1744         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1745         HBRUSH hbrush_old;\r
1746         int steps = squareSize;\r
1747         int i;\r
1748 \r
1749         triangle[0].x = squareSize - steps;\r
1750         triangle[0].y = squareSize;\r
1751         triangle[1].x = squareSize;\r
1752         triangle[1].y = squareSize;\r
1753         triangle[2].x = squareSize;\r
1754         triangle[2].y = squareSize - steps;\r
1755 \r
1756         for( i=0; i<steps; i++ ) {\r
1757             BYTE r = r1 - (r1-r2) * i / steps;\r
1758             BYTE g = g1 - (g1-g2) * i / steps;\r
1759             BYTE b = b1 - (b1-b2) * i / steps;\r
1760 \r
1761             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1762             hbrush_old = SelectObject( hdc, hbrush );\r
1763             Polygon( hdc, triangle, 3 );\r
1764             SelectObject( hdc, hbrush_old );\r
1765             DeleteObject(hbrush);\r
1766             triangle[0].x++;\r
1767             triangle[2].y++;\r
1768         }\r
1769 \r
1770         SelectObject( hdc, hpen );\r
1771     }\r
1772 }\r
1773 \r
1774 /*\r
1775     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1776     seems to work ok. The main problem here is to find the "inside" of a chess\r
1777     piece: follow the steps as explained below.\r
1778 */\r
1779 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1780 {\r
1781     HBITMAP hbm;\r
1782     HBITMAP hbm_old;\r
1783     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1784     RECT rc;\r
1785     SIZE sz;\r
1786     POINT pt;\r
1787     int backColor = whitePieceColor; \r
1788     int foreColor = blackPieceColor;\r
1789     \r
1790     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1791         backColor = appData.fontBackColorWhite;\r
1792         foreColor = appData.fontForeColorWhite;\r
1793     }\r
1794     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1795         backColor = appData.fontBackColorBlack;\r
1796         foreColor = appData.fontForeColorBlack;\r
1797     }\r
1798 \r
1799     /* Mask */\r
1800     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1801 \r
1802     hbm_old = SelectObject( hdc, hbm );\r
1803 \r
1804     rc.left = 0;\r
1805     rc.top = 0;\r
1806     rc.right = squareSize;\r
1807     rc.bottom = squareSize;\r
1808 \r
1809     /* Step 1: background is now black */\r
1810     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1811 \r
1812     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1813 \r
1814     pt.x = (squareSize - sz.cx) / 2;\r
1815     pt.y = (squareSize - sz.cy) / 2;\r
1816 \r
1817     SetBkMode( hdc, TRANSPARENT );\r
1818     SetTextColor( hdc, chroma );\r
1819     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1820     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1821 \r
1822     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1823     /* Step 3: the area outside the piece is filled with white */\r
1824 //    FloodFill( hdc, 0, 0, chroma );\r
1825     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1826     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1827     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1828     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1829     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1830     /* \r
1831         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1832         but if the start point is not inside the piece we're lost!\r
1833         There should be a better way to do this... if we could create a region or path\r
1834         from the fill operation we would be fine for example.\r
1835     */\r
1836 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1837     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1838 \r
1839     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1840         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1841         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1842 \r
1843         SelectObject( dc2, bm2 );\r
1844         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1845         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1846         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1847         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1848         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1849 \r
1850         DeleteDC( dc2 );\r
1851         DeleteObject( bm2 );\r
1852     }\r
1853 \r
1854     SetTextColor( hdc, 0 );\r
1855     /* \r
1856         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1857         draw the piece again in black for safety.\r
1858     */\r
1859     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1860 \r
1861     SelectObject( hdc, hbm_old );\r
1862 \r
1863     if( hPieceMask[index] != NULL ) {\r
1864         DeleteObject( hPieceMask[index] );\r
1865     }\r
1866 \r
1867     hPieceMask[index] = hbm;\r
1868 \r
1869     /* Face */\r
1870     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1871 \r
1872     SelectObject( hdc, hbm );\r
1873 \r
1874     {\r
1875         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1876         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1877         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1878 \r
1879         SelectObject( dc1, hPieceMask[index] );\r
1880         SelectObject( dc2, bm2 );\r
1881         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1882         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1883         \r
1884         /* \r
1885             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1886             the piece background and deletes (makes transparent) the rest.\r
1887             Thanks to that mask, we are free to paint the background with the greates\r
1888             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1889             We use this, to make gradients and give the pieces a "roundish" look.\r
1890         */\r
1891         SetPieceBackground( hdc, backColor, 2 );\r
1892         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1893 \r
1894         DeleteDC( dc2 );\r
1895         DeleteDC( dc1 );\r
1896         DeleteObject( bm2 );\r
1897     }\r
1898 \r
1899     SetTextColor( hdc, foreColor );\r
1900     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1901 \r
1902     SelectObject( hdc, hbm_old );\r
1903 \r
1904     if( hPieceFace[index] != NULL ) {\r
1905         DeleteObject( hPieceFace[index] );\r
1906     }\r
1907 \r
1908     hPieceFace[index] = hbm;\r
1909 }\r
1910 \r
1911 static int TranslatePieceToFontPiece( int piece )\r
1912 {\r
1913     switch( piece ) {\r
1914     case BlackPawn:\r
1915         return PM_BP;\r
1916     case BlackKnight:\r
1917         return PM_BN;\r
1918     case BlackBishop:\r
1919         return PM_BB;\r
1920     case BlackRook:\r
1921         return PM_BR;\r
1922     case BlackQueen:\r
1923         return PM_BQ;\r
1924     case BlackKing:\r
1925         return PM_BK;\r
1926     case WhitePawn:\r
1927         return PM_WP;\r
1928     case WhiteKnight:\r
1929         return PM_WN;\r
1930     case WhiteBishop:\r
1931         return PM_WB;\r
1932     case WhiteRook:\r
1933         return PM_WR;\r
1934     case WhiteQueen:\r
1935         return PM_WQ;\r
1936     case WhiteKing:\r
1937         return PM_WK;\r
1938 \r
1939     case BlackAngel:\r
1940         return PM_BA;\r
1941     case BlackMarshall:\r
1942         return PM_BC;\r
1943     case BlackFerz:\r
1944         return PM_BF;\r
1945     case BlackNightrider:\r
1946         return PM_BH;\r
1947     case BlackAlfil:\r
1948         return PM_BE;\r
1949     case BlackWazir:\r
1950         return PM_BW;\r
1951     case BlackUnicorn:\r
1952         return PM_BU;\r
1953     case BlackCannon:\r
1954         return PM_BO;\r
1955     case BlackGrasshopper:\r
1956         return PM_BG;\r
1957     case BlackMan:\r
1958         return PM_BM;\r
1959     case BlackSilver:\r
1960         return PM_BSG;\r
1961     case BlackLance:\r
1962         return PM_BL;\r
1963     case BlackFalcon:\r
1964         return PM_BV;\r
1965     case BlackCobra:\r
1966         return PM_BS;\r
1967     case BlackCardinal:\r
1968         return PM_BAB;\r
1969     case BlackDragon:\r
1970         return PM_BD;\r
1971 \r
1972     case WhiteAngel:\r
1973         return PM_WA;\r
1974     case WhiteMarshall:\r
1975         return PM_WC;\r
1976     case WhiteFerz:\r
1977         return PM_WF;\r
1978     case WhiteNightrider:\r
1979         return PM_WH;\r
1980     case WhiteAlfil:\r
1981         return PM_WE;\r
1982     case WhiteWazir:\r
1983         return PM_WW;\r
1984     case WhiteUnicorn:\r
1985         return PM_WU;\r
1986     case WhiteCannon:\r
1987         return PM_WO;\r
1988     case WhiteGrasshopper:\r
1989         return PM_WG;\r
1990     case WhiteMan:\r
1991         return PM_WM;\r
1992     case WhiteSilver:\r
1993         return PM_WSG;\r
1994     case WhiteLance:\r
1995         return PM_WL;\r
1996     case WhiteFalcon:\r
1997         return PM_WV;\r
1998     case WhiteCobra:\r
1999         return PM_WS;\r
2000     case WhiteCardinal:\r
2001         return PM_WAB;\r
2002     case WhiteDragon:\r
2003         return PM_WD;\r
2004     }\r
2005 \r
2006     return 0;\r
2007 }\r
2008 \r
2009 void CreatePiecesFromFont()\r
2010 {\r
2011     LOGFONT lf;\r
2012     HDC hdc_window = NULL;\r
2013     HDC hdc = NULL;\r
2014     HFONT hfont_old;\r
2015     int fontHeight;\r
2016     int i;\r
2017 \r
2018     if( fontBitmapSquareSize < 0 ) {\r
2019         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2020         return;\r
2021     }\r
2022 \r
2023     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2024             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2025         fontBitmapSquareSize = -1;\r
2026         return;\r
2027     }\r
2028 \r
2029     if( fontBitmapSquareSize != squareSize ) {\r
2030         hdc_window = GetDC( hwndMain );\r
2031         hdc = CreateCompatibleDC( hdc_window );\r
2032 \r
2033         if( hPieceFont != NULL ) {\r
2034             DeleteObject( hPieceFont );\r
2035         }\r
2036         else {\r
2037             for( i=0; i<=(int)BlackKing; i++ ) {\r
2038                 hPieceMask[i] = NULL;\r
2039                 hPieceFace[i] = NULL;\r
2040             }\r
2041         }\r
2042 \r
2043         fontHeight = 75;\r
2044 \r
2045         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2046             fontHeight = appData.fontPieceSize;\r
2047         }\r
2048 \r
2049         fontHeight = (fontHeight * squareSize) / 100;\r
2050 \r
2051         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2052         lf.lfWidth = 0;\r
2053         lf.lfEscapement = 0;\r
2054         lf.lfOrientation = 0;\r
2055         lf.lfWeight = FW_NORMAL;\r
2056         lf.lfItalic = 0;\r
2057         lf.lfUnderline = 0;\r
2058         lf.lfStrikeOut = 0;\r
2059         lf.lfCharSet = DEFAULT_CHARSET;\r
2060         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2061         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2062         lf.lfQuality = PROOF_QUALITY;\r
2063         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2064         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2065         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2066 \r
2067         hPieceFont = CreateFontIndirect( &lf );\r
2068 \r
2069         if( hPieceFont == NULL ) {\r
2070             fontBitmapSquareSize = -2;\r
2071         }\r
2072         else {\r
2073             /* Setup font-to-piece character table */\r
2074             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2075                 /* No (or wrong) global settings, try to detect the font */\r
2076                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2077                     /* Alpha */\r
2078                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2079                 }\r
2080                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2081                     /* DiagramTT* family */\r
2082                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2083                 }\r
2084                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2085                     /* Fairy symbols */\r
2086                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2087                 }\r
2088                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2089                     /* Good Companion (Some characters get warped as literal :-( */\r
2090                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2091                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2092                     SetCharTable(pieceToFontChar, s);\r
2093                 }\r
2094                 else {\r
2095                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2096                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2097                 }\r
2098             }\r
2099 \r
2100             /* Create bitmaps */\r
2101             hfont_old = SelectObject( hdc, hPieceFont );\r
2102             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2103                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2104                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2105 \r
2106             SelectObject( hdc, hfont_old );\r
2107 \r
2108             fontBitmapSquareSize = squareSize;\r
2109         }\r
2110     }\r
2111 \r
2112     if( hdc != NULL ) {\r
2113         DeleteDC( hdc );\r
2114     }\r
2115 \r
2116     if( hdc_window != NULL ) {\r
2117         ReleaseDC( hwndMain, hdc_window );\r
2118     }\r
2119 }\r
2120 \r
2121 HBITMAP\r
2122 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2123 {\r
2124   char name[128], buf[MSG_SIZ];\r
2125 \r
2126     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2127   if(appData.pieceDirectory[0]) {\r
2128     HBITMAP res;\r
2129     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2130     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2131     if(res) return res;\r
2132   }\r
2133   if (gameInfo.event &&\r
2134       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2135       strcmp(name, "k80s") == 0) {\r
2136     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2137   }\r
2138   return LoadBitmap(hinst, name);\r
2139 }\r
2140 \r
2141 \r
2142 /* Insert a color into the program's logical palette\r
2143    structure.  This code assumes the given color is\r
2144    the result of the RGB or PALETTERGB macro, and it\r
2145    knows how those macros work (which is documented).\r
2146 */\r
2147 VOID\r
2148 InsertInPalette(COLORREF color)\r
2149 {\r
2150   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2151 \r
2152   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2153     DisplayFatalError(_("Too many colors"), 0, 1);\r
2154     pLogPal->palNumEntries--;\r
2155     return;\r
2156   }\r
2157 \r
2158   pe->peFlags = (char) 0;\r
2159   pe->peRed = (char) (0xFF & color);\r
2160   pe->peGreen = (char) (0xFF & (color >> 8));\r
2161   pe->peBlue = (char) (0xFF & (color >> 16));\r
2162   return;\r
2163 }\r
2164 \r
2165 \r
2166 VOID\r
2167 InitDrawingColors()\r
2168 {\r
2169   int i;\r
2170   if (pLogPal == NULL) {\r
2171     /* Allocate enough memory for a logical palette with\r
2172      * PALETTESIZE entries and set the size and version fields\r
2173      * of the logical palette structure.\r
2174      */\r
2175     pLogPal = (NPLOGPALETTE)\r
2176       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2177                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2178     pLogPal->palVersion    = 0x300;\r
2179   }\r
2180   pLogPal->palNumEntries = 0;\r
2181 \r
2182   InsertInPalette(lightSquareColor);\r
2183   InsertInPalette(darkSquareColor);\r
2184   InsertInPalette(whitePieceColor);\r
2185   InsertInPalette(blackPieceColor);\r
2186   InsertInPalette(highlightSquareColor);\r
2187   InsertInPalette(premoveHighlightColor);\r
2188 \r
2189   /*  create a logical color palette according the information\r
2190    *  in the LOGPALETTE structure.\r
2191    */\r
2192   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2193 \r
2194   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2195   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2196   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2197   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2198   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2199   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2200   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2201     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2202 \r
2203    /* [AS] Force rendering of the font-based pieces */\r
2204   if( fontBitmapSquareSize > 0 ) {\r
2205     fontBitmapSquareSize = 0;\r
2206   }\r
2207 }\r
2208 \r
2209 \r
2210 int\r
2211 BoardWidth(int boardSize, int n)\r
2212 { /* [HGM] argument n added to allow different width and height */\r
2213   int lineGap = sizeInfo[boardSize].lineGap;\r
2214 \r
2215   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2216       lineGap = appData.overrideLineGap;\r
2217   }\r
2218 \r
2219   return (n + 1) * lineGap +\r
2220           n * sizeInfo[boardSize].squareSize;\r
2221 }\r
2222 \r
2223 /* Respond to board resize by dragging edge */\r
2224 VOID\r
2225 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2226 {\r
2227   BoardSize newSize = NUM_SIZES - 1;\r
2228   static int recurse = 0;\r
2229   if (IsIconic(hwndMain)) return;\r
2230   if (recurse > 0) return;\r
2231   recurse++;\r
2232   while (newSize > 0) {\r
2233         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2234         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2235            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2236     newSize--;\r
2237   } \r
2238   boardSize = newSize;\r
2239   InitDrawingSizes(boardSize, flags);\r
2240   recurse--;\r
2241 }\r
2242 \r
2243 \r
2244 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2245 \r
2246 VOID\r
2247 InitDrawingSizes(BoardSize boardSize, int flags)\r
2248 {\r
2249   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2250   ChessSquare piece;\r
2251   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2252   HDC hdc;\r
2253   SIZE clockSize, messageSize;\r
2254   HFONT oldFont;\r
2255   char buf[MSG_SIZ];\r
2256   char *str;\r
2257   HMENU hmenu = GetMenu(hwndMain);\r
2258   RECT crect, wrect, oldRect;\r
2259   int offby;\r
2260   LOGBRUSH logbrush;\r
2261   VariantClass v = gameInfo.variant;\r
2262 \r
2263   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2264   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2265 \r
2266   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2267   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2268   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2269   oldBoardSize = boardSize;\r
2270 \r
2271   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2272   { // correct board size to one where built-in pieces exist\r
2273     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2274        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2275       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2276       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2277       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2278       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2279       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2280                                    boardSize = SizeMiddling;\r
2281     }\r
2282   }\r
2283   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2284 \r
2285   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2286   oldRect.top = wpMain.y;\r
2287   oldRect.right = wpMain.x + wpMain.width;\r
2288   oldRect.bottom = wpMain.y + wpMain.height;\r
2289 \r
2290   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2291   smallLayout = sizeInfo[boardSize].smallLayout;\r
2292   squareSize = sizeInfo[boardSize].squareSize;\r
2293   lineGap = sizeInfo[boardSize].lineGap;\r
2294   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2295   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2296 \r
2297   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2298       lineGap = appData.overrideLineGap;\r
2299   }\r
2300 \r
2301   if (tinyLayout != oldTinyLayout) {\r
2302     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2303     if (tinyLayout) {\r
2304       style &= ~WS_SYSMENU;\r
2305       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2306                  "&Minimize\tCtrl+F4");\r
2307     } else {\r
2308       style |= WS_SYSMENU;\r
2309       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2310     }\r
2311     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2312 \r
2313     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2314       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2315         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2316     }\r
2317     DrawMenuBar(hwndMain);\r
2318   }\r
2319 \r
2320   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2321   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2322 \r
2323   /* Get text area sizes */\r
2324   hdc = GetDC(hwndMain);\r
2325   if (appData.clockMode) {\r
2326     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2327   } else {\r
2328     snprintf(buf, MSG_SIZ, _("White"));\r
2329   }\r
2330   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2331   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2332   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2333   str = _("We only care about the height here");\r
2334   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2335   SelectObject(hdc, oldFont);\r
2336   ReleaseDC(hwndMain, hdc);\r
2337 \r
2338   /* Compute where everything goes */\r
2339   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2340         /* [HGM] logo: if either logo is on, reserve space for it */\r
2341         logoHeight =  2*clockSize.cy;\r
2342         leftLogoRect.left   = OUTER_MARGIN;\r
2343         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2344         leftLogoRect.top    = OUTER_MARGIN;\r
2345         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2346 \r
2347         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2348         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2349         rightLogoRect.top    = OUTER_MARGIN;\r
2350         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2351 \r
2352 \r
2353     whiteRect.left = leftLogoRect.right;\r
2354     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2355     whiteRect.top = OUTER_MARGIN;\r
2356     whiteRect.bottom = whiteRect.top + logoHeight;\r
2357 \r
2358     blackRect.right = rightLogoRect.left;\r
2359     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2360     blackRect.top = whiteRect.top;\r
2361     blackRect.bottom = whiteRect.bottom;\r
2362   } else {\r
2363     whiteRect.left = OUTER_MARGIN;\r
2364     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2365     whiteRect.top = OUTER_MARGIN;\r
2366     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2367 \r
2368     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2369     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2370     blackRect.top = whiteRect.top;\r
2371     blackRect.bottom = whiteRect.bottom;\r
2372 \r
2373     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2374   }\r
2375 \r
2376   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2377   if (appData.showButtonBar) {\r
2378     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2379       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2380   } else {\r
2381     messageRect.right = OUTER_MARGIN + boardWidth;\r
2382   }\r
2383   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2384   messageRect.bottom = messageRect.top + messageSize.cy;\r
2385 \r
2386   boardRect.left = OUTER_MARGIN;\r
2387   boardRect.right = boardRect.left + boardWidth;\r
2388   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2389   boardRect.bottom = boardRect.top + boardHeight;\r
2390 \r
2391   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2392   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2393   oldTinyLayout = tinyLayout;\r
2394   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2395   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2396     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2397   winW *= 1 + twoBoards;\r
2398   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2399   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2400   wpMain.height = winH; //       without disturbing window attachments\r
2401   GetWindowRect(hwndMain, &wrect);\r
2402   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2403                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2404 \r
2405   // [HGM] placement: let attached windows follow size change.\r
2406   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2407   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2408   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2409   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2410   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2411 \r
2412   /* compensate if menu bar wrapped */\r
2413   GetClientRect(hwndMain, &crect);\r
2414   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2415   wpMain.height += offby;\r
2416   switch (flags) {\r
2417   case WMSZ_TOPLEFT:\r
2418     SetWindowPos(hwndMain, NULL, \r
2419                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2420                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2421     break;\r
2422 \r
2423   case WMSZ_TOPRIGHT:\r
2424   case WMSZ_TOP:\r
2425     SetWindowPos(hwndMain, NULL, \r
2426                  wrect.left, wrect.bottom - wpMain.height, \r
2427                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2428     break;\r
2429 \r
2430   case WMSZ_BOTTOMLEFT:\r
2431   case WMSZ_LEFT:\r
2432     SetWindowPos(hwndMain, NULL, \r
2433                  wrect.right - wpMain.width, wrect.top, \r
2434                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2435     break;\r
2436 \r
2437   case WMSZ_BOTTOMRIGHT:\r
2438   case WMSZ_BOTTOM:\r
2439   case WMSZ_RIGHT:\r
2440   default:\r
2441     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2442                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2443     break;\r
2444   }\r
2445 \r
2446   hwndPause = NULL;\r
2447   for (i = 0; i < N_BUTTONS; i++) {\r
2448     if (buttonDesc[i].hwnd != NULL) {\r
2449       DestroyWindow(buttonDesc[i].hwnd);\r
2450       buttonDesc[i].hwnd = NULL;\r
2451     }\r
2452     if (appData.showButtonBar) {\r
2453       buttonDesc[i].hwnd =\r
2454         CreateWindow("BUTTON", buttonDesc[i].label,\r
2455                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2456                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2457                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2458                      (HMENU) buttonDesc[i].id,\r
2459                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2460       if (tinyLayout) {\r
2461         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2462                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2463                     MAKELPARAM(FALSE, 0));\r
2464       }\r
2465       if (buttonDesc[i].id == IDM_Pause)\r
2466         hwndPause = buttonDesc[i].hwnd;\r
2467       buttonDesc[i].wndproc = (WNDPROC)\r
2468         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2469     }\r
2470   }\r
2471   if (gridPen != NULL) DeleteObject(gridPen);\r
2472   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2473   if (premovePen != NULL) DeleteObject(premovePen);\r
2474   if (lineGap != 0) {\r
2475     logbrush.lbStyle = BS_SOLID;\r
2476     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2477     gridPen =\r
2478       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2479                    lineGap, &logbrush, 0, NULL);\r
2480     logbrush.lbColor = highlightSquareColor;\r
2481     highlightPen =\r
2482       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2483                    lineGap, &logbrush, 0, NULL);\r
2484 \r
2485     logbrush.lbColor = premoveHighlightColor; \r
2486     premovePen =\r
2487       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2488                    lineGap, &logbrush, 0, NULL);\r
2489 \r
2490     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2491     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2492       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2493       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2494         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2495       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2496         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2497       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2498     }\r
2499     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2500       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2501       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2502         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2503         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2504       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2505         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2506       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2507     }\r
2508   }\r
2509 \r
2510   /* [HGM] Licensing requirement */\r
2511 #ifdef GOTHIC\r
2512   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2513 #endif\r
2514 #ifdef FALCON\r
2515   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2516 #endif\r
2517   GothicPopUp( "", VariantNormal);\r
2518 \r
2519 \r
2520 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2521 \r
2522   /* Load piece bitmaps for this board size */\r
2523   for (i=0; i<=2; i++) {\r
2524     for (piece = WhitePawn;\r
2525          (int) piece < (int) BlackPawn;\r
2526          piece = (ChessSquare) ((int) piece + 1)) {\r
2527       if (pieceBitmap[i][piece] != NULL)\r
2528         DeleteObject(pieceBitmap[i][piece]);\r
2529     }\r
2530   }\r
2531 \r
2532   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2533   // Orthodox Chess pieces\r
2534   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2535   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2536   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2537   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2538   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2539   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2540   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2541   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2542   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2543   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2544   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2545   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2546   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2547   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2548   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2549   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2550     // in Shogi, Hijack the unused Queen for Lance\r
2551     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2552     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2553     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2554   } else {\r
2555     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2556     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2557     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2558   }\r
2559 \r
2560   if(squareSize <= 72 && squareSize >= 33) { \r
2561     /* A & C are available in most sizes now */\r
2562     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2563       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2564       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2565       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2566       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2567       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2568       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2569       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2570       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2571       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2572       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2573       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2574       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2575     } else { // Smirf-like\r
2576       if(gameInfo.variant == VariantSChess) {\r
2577         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2578         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2579         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2580       } else {\r
2581         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2582         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2583         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2584       }\r
2585     }\r
2586     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2587       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2588       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2589       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2590     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2591       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2592       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2593       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2594     } else { // WinBoard standard\r
2595       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2596       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2597       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2598     }\r
2599   }\r
2600 \r
2601 \r
2602   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2603     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2604     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2605     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2606     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2607     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2608     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2609     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2610     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2611     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2612     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2613     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2614     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2615     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2616     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2617     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2618     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2619     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2620     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2621     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2622     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2623     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2624     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2625     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2626     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2627     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2628     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2629     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2630     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2631     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2632     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2633     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2634     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2635     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2636 \r
2637     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2638       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2639       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2640       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2641       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2642       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2643       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2644       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2645       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2646       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2647       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2648       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2649       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2650     } else {\r
2651       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2652       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2653       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2654       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2655       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2656       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2657       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2658       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2659       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2660       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2661       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2662       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2663     }\r
2664 \r
2665   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2666     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2667     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2668     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2669     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2670     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2671     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2672     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2673     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2674     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2675     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2676     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2677     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2678     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2679     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2680   }\r
2681 \r
2682 \r
2683   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2684   /* special Shogi support in this size */\r
2685   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2686       for (piece = WhitePawn;\r
2687            (int) piece < (int) BlackPawn;\r
2688            piece = (ChessSquare) ((int) piece + 1)) {\r
2689         if (pieceBitmap[i][piece] != NULL)\r
2690           DeleteObject(pieceBitmap[i][piece]);\r
2691       }\r
2692     }\r
2693   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2694   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2695   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2696   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2697   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2698   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2699   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2700   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2701   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2702   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2703   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2704   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2705   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2706   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2707   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2708   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2709   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2710   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2711   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2712   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2713   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2714   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2715   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2716   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2717   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2718   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2719   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2720   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2721   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2722   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2723   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2724   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2725   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2726   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2727   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2728   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2729   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2730   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2731   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2732   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2733   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2734   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2735   minorSize = 0;\r
2736   }\r
2737 }\r
2738 \r
2739 HBITMAP\r
2740 PieceBitmap(ChessSquare p, int kind)\r
2741 {\r
2742   if ((int) p >= (int) BlackPawn)\r
2743     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2744 \r
2745   return pieceBitmap[kind][(int) p];\r
2746 }\r
2747 \r
2748 /***************************************************************/\r
2749 \r
2750 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2751 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2752 /*\r
2753 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2754 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2755 */\r
2756 \r
2757 VOID\r
2758 SquareToPos(int row, int column, int * x, int * y)\r
2759 {\r
2760   if (flipView) {\r
2761     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2762     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2763   } else {\r
2764     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2765     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2766   }\r
2767 }\r
2768 \r
2769 VOID\r
2770 DrawCoordsOnDC(HDC hdc)\r
2771 {\r
2772   static char files[] = "0123456789012345678901221098765432109876543210";\r
2773   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2774   char str[2] = { NULLCHAR, NULLCHAR };\r
2775   int oldMode, oldAlign, x, y, start, i;\r
2776   HFONT oldFont;\r
2777   HBRUSH oldBrush;\r
2778 \r
2779   if (!appData.showCoords)\r
2780     return;\r
2781 \r
2782   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2783 \r
2784   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2785   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2786   oldAlign = GetTextAlign(hdc);\r
2787   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2788 \r
2789   y = boardRect.top + lineGap;\r
2790   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2791 \r
2792   if(border) {\r
2793     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2794     x += border - lineGap - 4; y += squareSize - 6;\r
2795   } else\r
2796   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2797   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2798     str[0] = files[start + i];\r
2799     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2800     y += squareSize + lineGap;\r
2801   }\r
2802 \r
2803   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2804 \r
2805   if(border) {\r
2806     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2807     x += -border + 4; y += border - squareSize + 6;\r
2808   } else\r
2809   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2810   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2811     str[0] = ranks[start + i];\r
2812     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2813     x += squareSize + lineGap;\r
2814   }    \r
2815 \r
2816   SelectObject(hdc, oldBrush);\r
2817   SetBkMode(hdc, oldMode);\r
2818   SetTextAlign(hdc, oldAlign);\r
2819   SelectObject(hdc, oldFont);\r
2820 }\r
2821 \r
2822 VOID\r
2823 DrawGridOnDC(HDC hdc)\r
2824 {\r
2825   HPEN oldPen;\r
2826  \r
2827   if (lineGap != 0) {\r
2828     oldPen = SelectObject(hdc, gridPen);\r
2829     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2830     SelectObject(hdc, oldPen);\r
2831   }\r
2832 }\r
2833 \r
2834 #define HIGHLIGHT_PEN 0\r
2835 #define PREMOVE_PEN   1\r
2836 \r
2837 VOID\r
2838 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2839 {\r
2840   int x1, y1;\r
2841   HPEN oldPen, hPen;\r
2842   if (lineGap == 0) return;\r
2843   if (flipView) {\r
2844     x1 = boardRect.left +\r
2845       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2846     y1 = boardRect.top +\r
2847       lineGap/2 + y * (squareSize + lineGap) + border;\r
2848   } else {\r
2849     x1 = boardRect.left +\r
2850       lineGap/2 + x * (squareSize + lineGap) + border;\r
2851     y1 = boardRect.top +\r
2852       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2853   }\r
2854   hPen = pen ? premovePen : highlightPen;\r
2855   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2856   MoveToEx(hdc, x1, y1, NULL);\r
2857   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2858   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2859   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2860   LineTo(hdc, x1, y1);\r
2861   SelectObject(hdc, oldPen);\r
2862 }\r
2863 \r
2864 VOID\r
2865 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2866 {\r
2867   int i;\r
2868   for (i=0; i<2; i++) {\r
2869     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2870       DrawHighlightOnDC(hdc, TRUE,\r
2871                         h->sq[i].x, h->sq[i].y,\r
2872                         pen);\r
2873   }\r
2874 }\r
2875 \r
2876 /* Note: sqcolor is used only in monoMode */\r
2877 /* Note that this code is largely duplicated in woptions.c,\r
2878    function DrawSampleSquare, so that needs to be updated too */\r
2879 VOID\r
2880 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2881 {\r
2882   HBITMAP oldBitmap;\r
2883   HBRUSH oldBrush;\r
2884   int tmpSize;\r
2885 \r
2886   if (appData.blindfold) return;\r
2887 \r
2888   /* [AS] Use font-based pieces if needed */\r
2889   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2890     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2891     CreatePiecesFromFont();\r
2892 \r
2893     if( fontBitmapSquareSize == squareSize ) {\r
2894         int index = TranslatePieceToFontPiece(piece);\r
2895 \r
2896         SelectObject( tmphdc, hPieceMask[ index ] );\r
2897 \r
2898       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2899         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2900       else\r
2901         BitBlt( hdc,\r
2902             x, y,\r
2903             squareSize, squareSize,\r
2904             tmphdc,\r
2905             0, 0,\r
2906             SRCAND );\r
2907 \r
2908         SelectObject( tmphdc, hPieceFace[ index ] );\r
2909 \r
2910       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2911         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2912       else\r
2913         BitBlt( hdc,\r
2914             x, y,\r
2915             squareSize, squareSize,\r
2916             tmphdc,\r
2917             0, 0,\r
2918             SRCPAINT );\r
2919 \r
2920         return;\r
2921     }\r
2922   }\r
2923 \r
2924   if (appData.monoMode) {\r
2925     SelectObject(tmphdc, PieceBitmap(piece, \r
2926       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2927     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2928            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2929   } else {\r
2930     HBRUSH xBrush = whitePieceBrush;\r
2931     tmpSize = squareSize;\r
2932     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2933     if(minorSize &&\r
2934         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2935          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2936       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2937       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2938       x += (squareSize - minorSize)>>1;\r
2939       y += squareSize - minorSize - 2;\r
2940       tmpSize = minorSize;\r
2941     }\r
2942     if (color || appData.allWhite ) {\r
2943       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2944       if( color )\r
2945               oldBrush = SelectObject(hdc, xBrush);\r
2946       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2947       if(appData.upsideDown && color==flipView)\r
2948         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2949       else\r
2950         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2951       /* Use black for outline of white pieces */\r
2952       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2953       if(appData.upsideDown && color==flipView)\r
2954         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2955       else\r
2956         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2957     } else if(appData.pieceDirectory[0]) {\r
2958       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2959       oldBrush = SelectObject(hdc, xBrush);\r
2960       if(appData.upsideDown && color==flipView)\r
2961         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2962       else\r
2963         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2964       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2965       if(appData.upsideDown && color==flipView)\r
2966         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2967       else\r
2968         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2969     } else {\r
2970       /* Use square color for details of black pieces */\r
2971       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2972       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2973       if(appData.upsideDown && !flipView)\r
2974         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2975       else\r
2976         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2977     }\r
2978     SelectObject(hdc, oldBrush);\r
2979     SelectObject(tmphdc, oldBitmap);\r
2980   }\r
2981 }\r
2982 \r
2983 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2984 int GetBackTextureMode( int algo )\r
2985 {\r
2986     int result = BACK_TEXTURE_MODE_DISABLED;\r
2987 \r
2988     switch( algo ) \r
2989     {\r
2990         case BACK_TEXTURE_MODE_PLAIN:\r
2991             result = 1; /* Always use identity map */\r
2992             break;\r
2993         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2994             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2995             break;\r
2996     }\r
2997 \r
2998     return result;\r
2999 }\r
3000 \r
3001 /* \r
3002     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3003     to handle redraws cleanly (as random numbers would always be different).\r
3004 */\r
3005 VOID RebuildTextureSquareInfo()\r
3006 {\r
3007     BITMAP bi;\r
3008     int lite_w = 0;\r
3009     int lite_h = 0;\r
3010     int dark_w = 0;\r
3011     int dark_h = 0;\r
3012     int row;\r
3013     int col;\r
3014 \r
3015     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3016 \r
3017     if( liteBackTexture != NULL ) {\r
3018         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3019             lite_w = bi.bmWidth;\r
3020             lite_h = bi.bmHeight;\r
3021         }\r
3022     }\r
3023 \r
3024     if( darkBackTexture != NULL ) {\r
3025         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3026             dark_w = bi.bmWidth;\r
3027             dark_h = bi.bmHeight;\r
3028         }\r
3029     }\r
3030 \r
3031     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3032         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3033             if( (col + row) & 1 ) {\r
3034                 /* Lite square */\r
3035                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3036                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3037                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3038                   else\r
3039                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3040                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3041                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3042                   else\r
3043                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3044                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3045                 }\r
3046             }\r
3047             else {\r
3048                 /* Dark square */\r
3049                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3050                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3051                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3052                   else\r
3053                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3054                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3055                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3056                   else\r
3057                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3058                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3059                 }\r
3060             }\r
3061         }\r
3062     }\r
3063 }\r
3064 \r
3065 /* [AS] Arrow highlighting support */\r
3066 \r
3067 static double A_WIDTH = 5; /* Width of arrow body */\r
3068 \r
3069 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3070 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3071 \r
3072 static double Sqr( double x )\r
3073 {\r
3074     return x*x;\r
3075 }\r
3076 \r
3077 static int Round( double x )\r
3078 {\r
3079     return (int) (x + 0.5);\r
3080 }\r
3081 \r
3082 /* Draw an arrow between two points using current settings */\r
3083 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3084 {\r
3085     POINT arrow[7];\r
3086     double dx, dy, j, k, x, y;\r
3087 \r
3088     if( d_x == s_x ) {\r
3089         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3090 \r
3091         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3092         arrow[0].y = s_y;\r
3093 \r
3094         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3095         arrow[1].y = d_y - h;\r
3096 \r
3097         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3098         arrow[2].y = d_y - h;\r
3099 \r
3100         arrow[3].x = d_x;\r
3101         arrow[3].y = d_y;\r
3102 \r
3103         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3104         arrow[5].y = d_y - h;\r
3105 \r
3106         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3107         arrow[4].y = d_y - h;\r
3108 \r
3109         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3110         arrow[6].y = s_y;\r
3111     }\r
3112     else if( d_y == s_y ) {\r
3113         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3114 \r
3115         arrow[0].x = s_x;\r
3116         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3117 \r
3118         arrow[1].x = d_x - w;\r
3119         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3120 \r
3121         arrow[2].x = d_x - w;\r
3122         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3123 \r
3124         arrow[3].x = d_x;\r
3125         arrow[3].y = d_y;\r
3126 \r
3127         arrow[5].x = d_x - w;\r
3128         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3129 \r
3130         arrow[4].x = d_x - w;\r
3131         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3132 \r
3133         arrow[6].x = s_x;\r
3134         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3135     }\r
3136     else {\r
3137         /* [AS] Needed a lot of paper for this! :-) */\r
3138         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3139         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3140   \r
3141         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3142 \r
3143         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3144 \r
3145         x = s_x;\r
3146         y = s_y;\r
3147 \r
3148         arrow[0].x = Round(x - j);\r
3149         arrow[0].y = Round(y + j*dx);\r
3150 \r
3151         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3152         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3153 \r
3154         if( d_x > s_x ) {\r
3155             x = (double) d_x - k;\r
3156             y = (double) d_y - k*dy;\r
3157         }\r
3158         else {\r
3159             x = (double) d_x + k;\r
3160             y = (double) d_y + k*dy;\r
3161         }\r
3162 \r
3163         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3164 \r
3165         arrow[6].x = Round(x - j);\r
3166         arrow[6].y = Round(y + j*dx);\r
3167 \r
3168         arrow[2].x = Round(arrow[6].x + 2*j);\r
3169         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3170 \r
3171         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3172         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3173 \r
3174         arrow[4].x = d_x;\r
3175         arrow[4].y = d_y;\r
3176 \r
3177         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3178         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3179     }\r
3180 \r
3181     Polygon( hdc, arrow, 7 );\r
3182 }\r
3183 \r
3184 /* [AS] Draw an arrow between two squares */\r
3185 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3186 {\r
3187     int s_x, s_y, d_x, d_y;\r
3188     HPEN hpen;\r
3189     HPEN holdpen;\r
3190     HBRUSH hbrush;\r
3191     HBRUSH holdbrush;\r
3192     LOGBRUSH stLB;\r
3193 \r
3194     if( s_col == d_col && s_row == d_row ) {\r
3195         return;\r
3196     }\r
3197 \r
3198     /* Get source and destination points */\r
3199     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3200     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3201 \r
3202     if( d_y > s_y ) {\r
3203         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3204     }\r
3205     else if( d_y < s_y ) {\r
3206         d_y += squareSize / 2 + squareSize / 4;\r
3207     }\r
3208     else {\r
3209         d_y += squareSize / 2;\r
3210     }\r
3211 \r
3212     if( d_x > s_x ) {\r
3213         d_x += squareSize / 2 - squareSize / 4;\r
3214     }\r
3215     else if( d_x < s_x ) {\r
3216         d_x += squareSize / 2 + squareSize / 4;\r
3217     }\r
3218     else {\r
3219         d_x += squareSize / 2;\r
3220     }\r
3221 \r
3222     s_x += squareSize / 2;\r
3223     s_y += squareSize / 2;\r
3224 \r
3225     /* Adjust width */\r
3226     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3227 \r
3228     /* Draw */\r
3229     stLB.lbStyle = BS_SOLID;\r
3230     stLB.lbColor = appData.highlightArrowColor;\r
3231     stLB.lbHatch = 0;\r
3232 \r
3233     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3234     holdpen = SelectObject( hdc, hpen );\r
3235     hbrush = CreateBrushIndirect( &stLB );\r
3236     holdbrush = SelectObject( hdc, hbrush );\r
3237 \r
3238     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3239 \r
3240     SelectObject( hdc, holdpen );\r
3241     SelectObject( hdc, holdbrush );\r
3242     DeleteObject( hpen );\r
3243     DeleteObject( hbrush );\r
3244 }\r
3245 \r
3246 BOOL HasHighlightInfo()\r
3247 {\r
3248     BOOL result = FALSE;\r
3249 \r
3250     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3251         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3252     {\r
3253         result = TRUE;\r
3254     }\r
3255 \r
3256     return result;\r
3257 \r
3258 \r
3259 \r
3260 }\r
3261 \r
3262 BOOL IsDrawArrowEnabled()\r
3263 {\r
3264     BOOL result = FALSE;\r
3265 \r
3266     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3267         result = TRUE;\r
3268     }\r
3269 \r
3270     return result;\r
3271 }\r
3272 \r
3273 VOID DrawArrowHighlight( HDC hdc )\r
3274 {\r
3275     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3276         DrawArrowBetweenSquares( hdc,\r
3277             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3278             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3279     }\r
3280 }\r
3281 \r
3282 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3283 {\r
3284     HRGN result = NULL;\r
3285 \r
3286     if( HasHighlightInfo() ) {\r
3287         int x1, y1, x2, y2;\r
3288         int sx, sy, dx, dy;\r
3289 \r
3290         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3291         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3292 \r
3293         sx = MIN( x1, x2 );\r
3294         sy = MIN( y1, y2 );\r
3295         dx = MAX( x1, x2 ) + squareSize;\r
3296         dy = MAX( y1, y2 ) + squareSize;\r
3297 \r
3298         result = CreateRectRgn( sx, sy, dx, dy );\r
3299     }\r
3300 \r
3301     return result;\r
3302 }\r
3303 \r
3304 /*\r
3305     Warning: this function modifies the behavior of several other functions. \r
3306     \r
3307     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3308     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3309     repaint is scattered all over the place, which is not good for features such as\r
3310     "arrow highlighting" that require a full repaint of the board.\r
3311 \r
3312     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3313     user interaction, when speed is not so important) but especially to avoid errors\r
3314     in the displayed graphics.\r
3315 \r
3316     In such patched places, I always try refer to this function so there is a single\r
3317     place to maintain knowledge.\r
3318     \r
3319     To restore the original behavior, just return FALSE unconditionally.\r
3320 */\r
3321 BOOL IsFullRepaintPreferrable()\r
3322 {\r
3323     BOOL result = FALSE;\r
3324 \r
3325     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3326         /* Arrow may appear on the board */\r
3327         result = TRUE;\r
3328     }\r
3329 \r
3330     return result;\r
3331 }\r
3332 \r
3333 /* \r
3334     This function is called by DrawPosition to know whether a full repaint must\r
3335     be forced or not.\r
3336 \r
3337     Only DrawPosition may directly call this function, which makes use of \r
3338     some state information. Other function should call DrawPosition specifying \r
3339     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3340 */\r
3341 BOOL DrawPositionNeedsFullRepaint()\r
3342 {\r
3343     BOOL result = FALSE;\r
3344 \r
3345     /* \r
3346         Probably a slightly better policy would be to trigger a full repaint\r
3347         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3348         but animation is fast enough that it's difficult to notice.\r
3349     */\r
3350     if( animInfo.piece == EmptySquare ) {\r
3351         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3352             result = TRUE;\r
3353         }\r
3354     }\r
3355 \r
3356     return result;\r
3357 }\r
3358 \r
3359 static HBITMAP borderBitmap;\r
3360 \r
3361 VOID\r
3362 DrawBackgroundOnDC(HDC hdc)\r
3363 {\r
3364   \r
3365   BITMAP bi;\r
3366   HDC tmphdc;\r
3367   HBITMAP hbm;\r
3368   static char oldBorder[MSG_SIZ];\r
3369   int w = 600, h = 600, mode;\r
3370 \r
3371   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3372     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3373     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3374   }\r
3375   if(borderBitmap == NULL) { // loading failed, use white\r
3376     FillRect( hdc, &boardRect, whitePieceBrush );\r
3377     return;\r
3378   }\r
3379   tmphdc = CreateCompatibleDC(hdc);\r
3380   hbm = SelectObject(tmphdc, borderBitmap);\r
3381   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3382             w = bi.bmWidth;\r
3383             h = bi.bmHeight;\r
3384   }\r
3385   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3386   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3387                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3388   SetStretchBltMode(hdc, mode);\r
3389   SelectObject(tmphdc, hbm);\r
3390   DeleteDC(tmphdc);\r
3391 }\r
3392 \r
3393 VOID\r
3394 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3395 {\r
3396   int row, column, x, y, square_color, piece_color;\r
3397   ChessSquare piece;\r
3398   HBRUSH oldBrush;\r
3399   HDC texture_hdc = NULL;\r
3400 \r
3401   /* [AS] Initialize background textures if needed */\r
3402   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3403       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3404       if( backTextureSquareSize != squareSize \r
3405        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3406           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3407           backTextureSquareSize = squareSize;\r
3408           RebuildTextureSquareInfo();\r
3409       }\r
3410 \r
3411       texture_hdc = CreateCompatibleDC( hdc );\r
3412   }\r
3413 \r
3414   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3415     for (column = 0; column < BOARD_WIDTH; column++) {\r
3416   \r
3417       SquareToPos(row, column, &x, &y);\r
3418 \r
3419       piece = board[row][column];\r
3420 \r
3421       square_color = ((column + row) % 2) == 1;\r
3422       if( gameInfo.variant == VariantXiangqi ) {\r
3423           square_color = !InPalace(row, column);\r
3424           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3425           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3426       }\r
3427       piece_color = (int) piece < (int) BlackPawn;\r
3428 \r
3429 \r
3430       /* [HGM] holdings file: light square or black */\r
3431       if(column == BOARD_LEFT-2) {\r
3432             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3433                 square_color = 1;\r
3434             else {\r
3435                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3436                 continue;\r
3437             }\r
3438       } else\r
3439       if(column == BOARD_RGHT + 1 ) {\r
3440             if( row < gameInfo.holdingsSize )\r
3441                 square_color = 1;\r
3442             else {\r
3443                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3444                 continue;\r
3445             }\r
3446       }\r
3447       if(column == BOARD_LEFT-1 ) /* left align */\r
3448             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3449       else if( column == BOARD_RGHT) /* right align */\r
3450             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3451       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3452       else\r
3453       if (appData.monoMode) {\r
3454         if (piece == EmptySquare) {\r
3455           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3456                  square_color ? WHITENESS : BLACKNESS);\r
3457         } else {\r
3458           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3459         }\r
3460       } \r
3461       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3462           /* [AS] Draw the square using a texture bitmap */\r
3463           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3464           int r = row, c = column; // [HGM] do not flip board in flipView\r
3465           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3466 \r
3467           DrawTile( x, y, \r
3468               squareSize, squareSize, \r
3469               hdc, \r
3470               texture_hdc,\r
3471               backTextureSquareInfo[r][c].mode,\r
3472               backTextureSquareInfo[r][c].x,\r
3473               backTextureSquareInfo[r][c].y );\r
3474 \r
3475           SelectObject( texture_hdc, hbm );\r
3476 \r
3477           if (piece != EmptySquare) {\r
3478               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3479           }\r
3480       }\r
3481       else {\r
3482         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3483 \r
3484         oldBrush = SelectObject(hdc, brush );\r
3485         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3486         SelectObject(hdc, oldBrush);\r
3487         if (piece != EmptySquare)\r
3488           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3489       }\r
3490     }\r
3491   }\r
3492 \r
3493   if( texture_hdc != NULL ) {\r
3494     DeleteDC( texture_hdc );\r
3495   }\r
3496 }\r
3497 \r
3498 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3499 void fputDW(FILE *f, int x)\r
3500 {\r
3501         fputc(x     & 255, f);\r
3502         fputc(x>>8  & 255, f);\r
3503         fputc(x>>16 & 255, f);\r
3504         fputc(x>>24 & 255, f);\r
3505 }\r
3506 \r
3507 #define MAX_CLIPS 200   /* more than enough */\r
3508 \r
3509 VOID\r
3510 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3511 {\r
3512 //  HBITMAP bufferBitmap;\r
3513   BITMAP bi;\r
3514 //  RECT Rect;\r
3515   HDC tmphdc;\r
3516   HBITMAP hbm;\r
3517   int w = 100, h = 50;\r
3518 \r
3519   if(logo == NULL) {\r
3520     if(!logoHeight) return;\r
3521     FillRect( hdc, &logoRect, whitePieceBrush );\r
3522   }\r
3523 //  GetClientRect(hwndMain, &Rect);\r
3524 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3525 //                                      Rect.bottom-Rect.top+1);\r
3526   tmphdc = CreateCompatibleDC(hdc);\r
3527   hbm = SelectObject(tmphdc, logo);\r
3528   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3529             w = bi.bmWidth;\r
3530             h = bi.bmHeight;\r
3531   }\r
3532   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3533                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3534   SelectObject(tmphdc, hbm);\r
3535   DeleteDC(tmphdc);\r
3536 }\r
3537 \r
3538 VOID\r
3539 DisplayLogos()\r
3540 {\r
3541   if(logoHeight) {\r
3542         HDC hdc = GetDC(hwndMain);\r
3543         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3544         if(appData.autoLogo) {\r
3545           \r
3546           switch(gameMode) { // pick logos based on game mode\r
3547             case IcsObserving:\r
3548                 whiteLogo = second.programLogo; // ICS logo\r
3549                 blackLogo = second.programLogo;\r
3550             default:\r
3551                 break;\r
3552             case IcsPlayingWhite:\r
3553                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3554                 blackLogo = second.programLogo; // ICS logo\r
3555                 break;\r
3556             case IcsPlayingBlack:\r
3557                 whiteLogo = second.programLogo; // ICS logo\r
3558                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3559                 break;\r
3560             case TwoMachinesPlay:\r
3561                 if(first.twoMachinesColor[0] == 'b') {\r
3562                     whiteLogo = second.programLogo;\r
3563                     blackLogo = first.programLogo;\r
3564                 }\r
3565                 break;\r
3566             case MachinePlaysWhite:\r
3567                 blackLogo = userLogo;\r
3568                 break;\r
3569             case MachinePlaysBlack:\r
3570                 whiteLogo = userLogo;\r
3571                 blackLogo = first.programLogo;\r
3572           }\r
3573         }\r
3574         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3575         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3576         ReleaseDC(hwndMain, hdc);\r
3577   }\r
3578 }\r
3579 \r
3580 void\r
3581 UpdateLogos(int display)\r
3582 { // called after loading new engine(s), in tourney or from menu\r
3583   LoadLogo(&first, 0, FALSE);\r
3584   LoadLogo(&second, 1, appData.icsActive);\r
3585   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3586   if(display) DisplayLogos();\r
3587 }\r
3588 \r
3589 static HDC hdcSeek;\r
3590 \r
3591 // [HGM] seekgraph\r
3592 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3593 {\r
3594     POINT stPt;\r
3595     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3596     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3597     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3598     SelectObject( hdcSeek, hp );\r
3599 }\r
3600 \r
3601 // front-end wrapper for drawing functions to do rectangles\r
3602 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3603 {\r
3604     HPEN hp;\r
3605     RECT rc;\r
3606 \r
3607     if (hdcSeek == NULL) {\r
3608     hdcSeek = GetDC(hwndMain);\r
3609       if (!appData.monoMode) {\r
3610         SelectPalette(hdcSeek, hPal, FALSE);\r
3611         RealizePalette(hdcSeek);\r
3612       }\r
3613     }\r
3614     hp = SelectObject( hdcSeek, gridPen );\r
3615     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3616     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3617     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3618     SelectObject( hdcSeek, hp );\r
3619 }\r
3620 \r
3621 // front-end wrapper for putting text in graph\r
3622 void DrawSeekText(char *buf, int x, int y)\r
3623 {\r
3624         SIZE stSize;\r
3625         SetBkMode( hdcSeek, TRANSPARENT );\r
3626         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3627         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3628 }\r
3629 \r
3630 void DrawSeekDot(int x, int y, int color)\r
3631 {\r
3632         int square = color & 0x80;\r
3633         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3634                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3635         color &= 0x7F;\r
3636         if(square)\r
3637             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3638                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3639         else\r
3640             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3641                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3642             SelectObject(hdcSeek, oldBrush);\r
3643 }\r
3644 \r
3645 void DrawSeekOpen()\r
3646 {\r
3647 }\r
3648 \r
3649 void DrawSeekClose()\r
3650 {\r
3651 }\r
3652 \r
3653 VOID\r
3654 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3655 {\r
3656   static Board lastReq[2], lastDrawn[2];\r
3657   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3658   static int lastDrawnFlipView = 0;\r
3659   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3660   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3661   HDC tmphdc;\r
3662   HDC hdcmem;\r
3663   HBITMAP bufferBitmap;\r
3664   HBITMAP oldBitmap;\r
3665   RECT Rect;\r
3666   HRGN clips[MAX_CLIPS];\r
3667   ChessSquare dragged_piece = EmptySquare;\r
3668   int nr = twoBoards*partnerUp;\r
3669 \r
3670   /* I'm undecided on this - this function figures out whether a full\r
3671    * repaint is necessary on its own, so there's no real reason to have the\r
3672    * caller tell it that.  I think this can safely be set to FALSE - but\r
3673    * if we trust the callers not to request full repaints unnessesarily, then\r
3674    * we could skip some clipping work.  In other words, only request a full\r
3675    * redraw when the majority of pieces have changed positions (ie. flip, \r
3676    * gamestart and similar)  --Hawk\r
3677    */\r
3678   Boolean fullrepaint = repaint;\r
3679 \r
3680   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3681 \r
3682   if( DrawPositionNeedsFullRepaint() ) {\r
3683       fullrepaint = TRUE;\r
3684   }\r
3685 \r
3686   if (board == NULL) {\r
3687     if (!lastReqValid[nr]) {\r
3688       return;\r
3689     }\r
3690     board = lastReq[nr];\r
3691   } else {\r
3692     CopyBoard(lastReq[nr], board);\r
3693     lastReqValid[nr] = 1;\r
3694   }\r
3695 \r
3696   if (doingSizing) {\r
3697     return;\r
3698   }\r
3699 \r
3700   if (IsIconic(hwndMain)) {\r
3701     return;\r
3702   }\r
3703 \r
3704   if (hdc == NULL) {\r
3705     hdc = GetDC(hwndMain);\r
3706     if (!appData.monoMode) {\r
3707       SelectPalette(hdc, hPal, FALSE);\r
3708       RealizePalette(hdc);\r
3709     }\r
3710     releaseDC = TRUE;\r
3711   } else {\r
3712     releaseDC = FALSE;\r
3713   }\r
3714 \r
3715   /* Create some work-DCs */\r
3716   hdcmem = CreateCompatibleDC(hdc);\r
3717   tmphdc = CreateCompatibleDC(hdc);\r
3718 \r
3719   /* If dragging is in progress, we temporarely remove the piece */\r
3720   /* [HGM] or temporarily decrease count if stacked              */\r
3721   /*       !! Moved to before board compare !!                   */\r
3722   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3723     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3724     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3725             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3726         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3727     } else \r
3728     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3729             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3730         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3731     } else \r
3732         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3733   }\r
3734 \r
3735   /* Figure out which squares need updating by comparing the \r
3736    * newest board with the last drawn board and checking if\r
3737    * flipping has changed.\r
3738    */\r
3739   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3740     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3741       for (column = 0; column < BOARD_WIDTH; column++) {\r
3742         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3743           SquareToPos(row, column, &x, &y);\r
3744           clips[num_clips++] =\r
3745             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3746         }\r
3747       }\r
3748     }\r
3749    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3750     for (i=0; i<2; i++) {\r
3751       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3752           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3753         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3754             lastDrawnHighlight.sq[i].y >= 0) {\r
3755           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3756                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3757           clips[num_clips++] =\r
3758             CreateRectRgn(x - lineGap, y - lineGap, \r
3759                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3760         }\r
3761         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3762           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3763           clips[num_clips++] =\r
3764             CreateRectRgn(x - lineGap, y - lineGap, \r
3765                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3766         }\r
3767       }\r
3768     }\r
3769     for (i=0; i<2; i++) {\r
3770       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3771           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3772         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3773             lastDrawnPremove.sq[i].y >= 0) {\r
3774           SquareToPos(lastDrawnPremove.sq[i].y,\r
3775                       lastDrawnPremove.sq[i].x, &x, &y);\r
3776           clips[num_clips++] =\r
3777             CreateRectRgn(x - lineGap, y - lineGap, \r
3778                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3779         }\r
3780         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3781             premoveHighlightInfo.sq[i].y >= 0) {\r
3782           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3783                       premoveHighlightInfo.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       }\r
3789     }\r
3790    } else { // nr == 1\r
3791         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3792         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3793         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3794         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3795       for (i=0; i<2; i++) {\r
3796         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3797             partnerHighlightInfo.sq[i].y >= 0) {\r
3798           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3799                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3800           clips[num_clips++] =\r
3801             CreateRectRgn(x - lineGap, y - lineGap, \r
3802                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3803         }\r
3804         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3805             oldPartnerHighlight.sq[i].y >= 0) {\r
3806           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3807                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3808           clips[num_clips++] =\r
3809             CreateRectRgn(x - lineGap, y - lineGap, \r
3810                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3811         }\r
3812       }\r
3813    }\r
3814   } else {\r
3815     fullrepaint = TRUE;\r
3816   }\r
3817 \r
3818   /* Create a buffer bitmap - this is the actual bitmap\r
3819    * being written to.  When all the work is done, we can\r
3820    * copy it to the real DC (the screen).  This avoids\r
3821    * the problems with flickering.\r
3822    */\r
3823   GetClientRect(hwndMain, &Rect);\r
3824   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3825                                         Rect.bottom-Rect.top+1);\r
3826   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3827   if (!appData.monoMode) {\r
3828     SelectPalette(hdcmem, hPal, FALSE);\r
3829   }\r
3830 \r
3831   /* Create clips for dragging */\r
3832   if (!fullrepaint) {\r
3833     if (dragInfo.from.x >= 0) {\r
3834       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3835       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3836     }\r
3837     if (dragInfo.start.x >= 0) {\r
3838       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3839       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3840     }\r
3841     if (dragInfo.pos.x >= 0) {\r
3842       x = dragInfo.pos.x - squareSize / 2;\r
3843       y = dragInfo.pos.y - squareSize / 2;\r
3844       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3845     }\r
3846     if (dragInfo.lastpos.x >= 0) {\r
3847       x = dragInfo.lastpos.x - squareSize / 2;\r
3848       y = dragInfo.lastpos.y - squareSize / 2;\r
3849       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3850     }\r
3851   }\r
3852 \r
3853   /* Are we animating a move?  \r
3854    * If so, \r
3855    *   - remove the piece from the board (temporarely)\r
3856    *   - calculate the clipping region\r
3857    */\r
3858   if (!fullrepaint) {\r
3859     if (animInfo.piece != EmptySquare) {\r
3860       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3861       x = boardRect.left + animInfo.lastpos.x;\r
3862       y = boardRect.top + animInfo.lastpos.y;\r
3863       x2 = boardRect.left + animInfo.pos.x;\r
3864       y2 = boardRect.top + animInfo.pos.y;\r
3865       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3866       /* Slight kludge.  The real problem is that after AnimateMove is\r
3867          done, the position on the screen does not match lastDrawn.\r
3868          This currently causes trouble only on e.p. captures in\r
3869          atomic, where the piece moves to an empty square and then\r
3870          explodes.  The old and new positions both had an empty square\r
3871          at the destination, but animation has drawn a piece there and\r
3872          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3873       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3874     }\r
3875   }\r
3876 \r
3877   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3878   if (num_clips == 0)\r
3879     fullrepaint = TRUE;\r
3880 \r
3881   /* Set clipping on the memory DC */\r
3882   if (!fullrepaint) {\r
3883     SelectClipRgn(hdcmem, clips[0]);\r
3884     for (x = 1; x < num_clips; x++) {\r
3885       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3886         abort();  // this should never ever happen!\r
3887     }\r
3888   }\r
3889 \r
3890   /* Do all the drawing to the memory DC */\r
3891   if(explodeInfo.radius) { // [HGM] atomic\r
3892         HBRUSH oldBrush;\r
3893         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3894         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3895         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3896         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3897         x += squareSize/2;\r
3898         y += squareSize/2;\r
3899         if(!fullrepaint) {\r
3900           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3901           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3902         }\r
3903         DrawGridOnDC(hdcmem);\r
3904         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3905         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3906         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3907         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3908         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3909         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3910         SelectObject(hdcmem, oldBrush);\r
3911   } else {\r
3912     if(border) DrawBackgroundOnDC(hdcmem);\r
3913     DrawGridOnDC(hdcmem);\r
3914     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3915         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3916         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3917     } else {\r
3918         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3919         oldPartnerHighlight = partnerHighlightInfo;\r
3920     }\r
3921     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3922   }\r
3923   if(nr == 0) // [HGM] dual: markers only on left board\r
3924   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3925     for (column = 0; column < BOARD_WIDTH; column++) {\r
3926         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3927             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3928             SquareToPos(row, column, &x, &y);\r
3929             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3930                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3931             SelectObject(hdcmem, oldBrush);\r
3932         }\r
3933     }\r
3934   }\r
3935 \r
3936   if( appData.highlightMoveWithArrow ) {\r
3937     DrawArrowHighlight(hdcmem);\r
3938   }\r
3939 \r
3940   DrawCoordsOnDC(hdcmem);\r
3941 \r
3942   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3943                  /* to make sure lastDrawn contains what is actually drawn */\r
3944 \r
3945   /* Put the dragged piece back into place and draw it (out of place!) */\r
3946     if (dragged_piece != EmptySquare) {\r
3947     /* [HGM] or restack */\r
3948     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3949                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3950     else\r
3951     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3952                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3953     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3954     x = dragInfo.pos.x - squareSize / 2;\r
3955     y = dragInfo.pos.y - squareSize / 2;\r
3956     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3957                   ((int) dragInfo.piece < (int) BlackPawn), \r
3958                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3959   }   \r
3960   \r
3961   /* Put the animated piece back into place and draw it */\r
3962   if (animInfo.piece != EmptySquare) {\r
3963     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3964     x = boardRect.left + animInfo.pos.x;\r
3965     y = boardRect.top + animInfo.pos.y;\r
3966     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3967                   ((int) animInfo.piece < (int) BlackPawn),\r
3968                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3969   }\r
3970 \r
3971   /* Release the bufferBitmap by selecting in the old bitmap \r
3972    * and delete the memory DC\r
3973    */\r
3974   SelectObject(hdcmem, oldBitmap);\r
3975   DeleteDC(hdcmem);\r
3976 \r
3977   /* Set clipping on the target DC */\r
3978   if (!fullrepaint) {\r
3979     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3980         RECT rect;\r
3981         GetRgnBox(clips[x], &rect);\r
3982         DeleteObject(clips[x]);\r
3983         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3984                           rect.right + wpMain.width/2, rect.bottom);\r
3985     }\r
3986     SelectClipRgn(hdc, clips[0]);\r
3987     for (x = 1; x < num_clips; x++) {\r
3988       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3989         abort();   // this should never ever happen!\r
3990     } \r
3991   }\r
3992 \r
3993   /* Copy the new bitmap onto the screen in one go.\r
3994    * This way we avoid any flickering\r
3995    */\r
3996   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3997   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3998          boardRect.right - boardRect.left,\r
3999          boardRect.bottom - boardRect.top,\r
4000          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4001   if(saveDiagFlag) { \r
4002     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4003     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4004 \r
4005     GetObject(bufferBitmap, sizeof(b), &b);\r
4006     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4007         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4008         bih.biWidth = b.bmWidth;\r
4009         bih.biHeight = b.bmHeight;\r
4010         bih.biPlanes = 1;\r
4011         bih.biBitCount = b.bmBitsPixel;\r
4012         bih.biCompression = 0;\r
4013         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4014         bih.biXPelsPerMeter = 0;\r
4015         bih.biYPelsPerMeter = 0;\r
4016         bih.biClrUsed = 0;\r
4017         bih.biClrImportant = 0;\r
4018 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4019 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4020         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4021 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4022 \r
4023         wb = b.bmWidthBytes;\r
4024         // count colors\r
4025         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4026                 int k = ((int*) pData)[i];\r
4027                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4028                 if(j >= 16) break;\r
4029                 color[j] = k;\r
4030                 if(j >= nrColors) nrColors = j+1;\r
4031         }\r
4032         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4033                 INT p = 0;\r
4034                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4035                     for(w=0; w<(wb>>2); w+=2) {\r
4036                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4037                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4038                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4039                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4040                         pData[p++] = m | j<<4;\r
4041                     }\r
4042                     while(p&3) pData[p++] = 0;\r
4043                 }\r
4044                 fac = 3;\r
4045                 wb = ((wb+31)>>5)<<2;\r
4046         }\r
4047         // write BITMAPFILEHEADER\r
4048         fprintf(diagFile, "BM");\r
4049         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4050         fputDW(diagFile, 0);\r
4051         fputDW(diagFile, 0x36 + (fac?64:0));\r
4052         // write BITMAPINFOHEADER\r
4053         fputDW(diagFile, 40);\r
4054         fputDW(diagFile, b.bmWidth);\r
4055         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4056         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4057         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4058         fputDW(diagFile, 0);\r
4059         fputDW(diagFile, 0);\r
4060         fputDW(diagFile, 0);\r
4061         fputDW(diagFile, 0);\r
4062         fputDW(diagFile, 0);\r
4063         fputDW(diagFile, 0);\r
4064         // write color table\r
4065         if(fac)\r
4066         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4067         // write bitmap data\r
4068         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4069                 fputc(pData[i], diagFile);\r
4070         free(pData);\r
4071      }\r
4072   }\r
4073 \r
4074   SelectObject(tmphdc, oldBitmap);\r
4075 \r
4076   /* Massive cleanup */\r
4077   for (x = 0; x < num_clips; x++)\r
4078     DeleteObject(clips[x]);\r
4079 \r
4080   DeleteDC(tmphdc);\r
4081   DeleteObject(bufferBitmap);\r
4082 \r
4083   if (releaseDC) \r
4084     ReleaseDC(hwndMain, hdc);\r
4085   \r
4086   if (lastDrawnFlipView != flipView && nr == 0) {\r
4087     if (flipView)\r
4088       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4089     else\r
4090       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4091   }\r
4092 \r
4093 /*  CopyBoard(lastDrawn, board);*/\r
4094   lastDrawnHighlight = highlightInfo;\r
4095   lastDrawnPremove   = premoveHighlightInfo;\r
4096   lastDrawnFlipView = flipView;\r
4097   lastDrawnValid[nr] = 1;\r
4098 }\r
4099 \r
4100 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4101 int\r
4102 SaveDiagram(f)\r
4103      FILE *f;\r
4104 {\r
4105     saveDiagFlag = 1; diagFile = f;\r
4106     HDCDrawPosition(NULL, TRUE, NULL);\r
4107     saveDiagFlag = 0;\r
4108 \r
4109     fclose(f);\r
4110     return TRUE;\r
4111 }\r
4112 \r
4113 \r
4114 /*---------------------------------------------------------------------------*\\r
4115 | CLIENT PAINT PROCEDURE\r
4116 |   This is the main event-handler for the WM_PAINT message.\r
4117 |\r
4118 \*---------------------------------------------------------------------------*/\r
4119 VOID\r
4120 PaintProc(HWND hwnd)\r
4121 {\r
4122   HDC         hdc;\r
4123   PAINTSTRUCT ps;\r
4124   HFONT       oldFont;\r
4125 \r
4126   if((hdc = BeginPaint(hwnd, &ps))) {\r
4127     if (IsIconic(hwnd)) {\r
4128       DrawIcon(hdc, 2, 2, iconCurrent);\r
4129     } else {\r
4130       if (!appData.monoMode) {\r
4131         SelectPalette(hdc, hPal, FALSE);\r
4132         RealizePalette(hdc);\r
4133       }\r
4134       HDCDrawPosition(hdc, 1, NULL);\r
4135       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4136         flipView = !flipView; partnerUp = !partnerUp;\r
4137         HDCDrawPosition(hdc, 1, NULL);\r
4138         flipView = !flipView; partnerUp = !partnerUp;\r
4139       }\r
4140       oldFont =\r
4141         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4142       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4143                  ETO_CLIPPED|ETO_OPAQUE,\r
4144                  &messageRect, messageText, strlen(messageText), NULL);\r
4145       SelectObject(hdc, oldFont);\r
4146       DisplayBothClocks();\r
4147       DisplayLogos();\r
4148     }\r
4149     EndPaint(hwnd,&ps);\r
4150   }\r
4151 \r
4152   return;\r
4153 }\r
4154 \r
4155 \r
4156 /*\r
4157  * If the user selects on a border boundary, return -1; if off the board,\r
4158  *   return -2.  Otherwise map the event coordinate to the square.\r
4159  * The offset boardRect.left or boardRect.top must already have been\r
4160  *   subtracted from x.\r
4161  */\r
4162 int EventToSquare(x, limit)\r
4163      int x, limit;\r
4164 {\r
4165   if (x <= border)\r
4166     return -2;\r
4167   if (x < lineGap + border)\r
4168     return -1;\r
4169   x -= lineGap + border;\r
4170   if ((x % (squareSize + lineGap)) >= squareSize)\r
4171     return -1;\r
4172   x /= (squareSize + lineGap);\r
4173     if (x >= limit)\r
4174     return -2;\r
4175   return x;\r
4176 }\r
4177 \r
4178 typedef struct {\r
4179   char piece;\r
4180   int command;\r
4181   char* name;\r
4182 } DropEnable;\r
4183 \r
4184 DropEnable dropEnables[] = {\r
4185   { 'P', DP_Pawn, N_("Pawn") },\r
4186   { 'N', DP_Knight, N_("Knight") },\r
4187   { 'B', DP_Bishop, N_("Bishop") },\r
4188   { 'R', DP_Rook, N_("Rook") },\r
4189   { 'Q', DP_Queen, N_("Queen") },\r
4190 };\r
4191 \r
4192 VOID\r
4193 SetupDropMenu(HMENU hmenu)\r
4194 {\r
4195   int i, count, enable;\r
4196   char *p;\r
4197   extern char white_holding[], black_holding[];\r
4198   char item[MSG_SIZ];\r
4199 \r
4200   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4201     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4202                dropEnables[i].piece);\r
4203     count = 0;\r
4204     while (p && *p++ == dropEnables[i].piece) count++;\r
4205       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4206     enable = count > 0 || !appData.testLegality\r
4207       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4208                       && !appData.icsActive);\r
4209     ModifyMenu(hmenu, dropEnables[i].command,\r
4210                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4211                dropEnables[i].command, item);\r
4212   }\r
4213 }\r
4214 \r
4215 void DragPieceBegin(int x, int y, Boolean instantly)\r
4216 {\r
4217       dragInfo.lastpos.x = boardRect.left + x;\r
4218       dragInfo.lastpos.y = boardRect.top + y;\r
4219       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4220       dragInfo.from.x = fromX;\r
4221       dragInfo.from.y = fromY;\r
4222       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4223       dragInfo.start = dragInfo.from;\r
4224       SetCapture(hwndMain);\r
4225 }\r
4226 \r
4227 void DragPieceEnd(int x, int y)\r
4228 {\r
4229     ReleaseCapture();\r
4230     dragInfo.start.x = dragInfo.start.y = -1;\r
4231     dragInfo.from = dragInfo.start;\r
4232     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4233 }\r
4234 \r
4235 void ChangeDragPiece(ChessSquare piece)\r
4236 {\r
4237     dragInfo.piece = piece;\r
4238 }\r
4239 \r
4240 /* Event handler for mouse messages */\r
4241 VOID\r
4242 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4243 {\r
4244   int x, y, menuNr;\r
4245   POINT pt;\r
4246   static int recursive = 0;\r
4247   HMENU hmenu;\r
4248   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4249 \r
4250   if (recursive) {\r
4251     if (message == WM_MBUTTONUP) {\r
4252       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4253          to the middle button: we simulate pressing the left button too!\r
4254          */\r
4255       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4256       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4257     }\r
4258     return;\r
4259   }\r
4260   recursive++;\r
4261   \r
4262   pt.x = LOWORD(lParam);\r
4263   pt.y = HIWORD(lParam);\r
4264   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4265   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4266   if (!flipView && y >= 0) {\r
4267     y = BOARD_HEIGHT - 1 - y;\r
4268   }\r
4269   if (flipView && x >= 0) {\r
4270     x = BOARD_WIDTH - 1 - x;\r
4271   }\r
4272 \r
4273   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4274   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4275 \r
4276   switch (message) {\r
4277   case WM_LBUTTONDOWN:\r
4278       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4279         ClockClick(flipClock); break;\r
4280       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4281         ClockClick(!flipClock); break;\r
4282       }\r
4283     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4284       dragInfo.start.x = dragInfo.start.y = -1;\r
4285       dragInfo.from = dragInfo.start;\r
4286     }\r
4287     if(fromX == -1 && frozen) { // not sure where this is for\r
4288                 fromX = fromY = -1; \r
4289       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4290       break;\r
4291     }\r
4292       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4293       DrawPosition(TRUE, NULL);\r
4294     break;\r
4295 \r
4296   case WM_LBUTTONUP:\r
4297       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4298       DrawPosition(TRUE, NULL);\r
4299     break;\r
4300 \r
4301   case WM_MOUSEMOVE:\r
4302     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4303     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4304     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4305     if ((appData.animateDragging || appData.highlightDragging)\r
4306         && (wParam & MK_LBUTTON || dragging == 2)\r
4307         && dragInfo.from.x >= 0) \r
4308     {\r
4309       BOOL full_repaint = FALSE;\r
4310 \r
4311       if (appData.animateDragging) {\r
4312         dragInfo.pos = pt;\r
4313       }\r
4314       if (appData.highlightDragging) {\r
4315         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4316         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4317             full_repaint = TRUE;\r
4318         }\r
4319       }\r
4320       \r
4321       DrawPosition( full_repaint, NULL);\r
4322       \r
4323       dragInfo.lastpos = dragInfo.pos;\r
4324     }\r
4325     break;\r
4326 \r
4327   case WM_MOUSEWHEEL: // [DM]\r
4328     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4329        /* Mouse Wheel is being rolled forward\r
4330         * Play moves forward\r
4331         */\r
4332        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4333                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4334        /* Mouse Wheel is being rolled backward\r
4335         * Play moves backward\r
4336         */\r
4337        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4338                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4339     }\r
4340     break;\r
4341 \r
4342   case WM_MBUTTONUP:\r
4343   case WM_RBUTTONUP:\r
4344     ReleaseCapture();\r
4345     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4346     break;\r
4347  \r
4348   case WM_MBUTTONDOWN:\r
4349   case WM_RBUTTONDOWN:\r
4350     ErrorPopDown();\r
4351     ReleaseCapture();\r
4352     fromX = fromY = -1;\r
4353     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4354     dragInfo.start.x = dragInfo.start.y = -1;\r
4355     dragInfo.from = dragInfo.start;\r
4356     dragInfo.lastpos = dragInfo.pos;\r
4357     if (appData.highlightDragging) {\r
4358       ClearHighlights();\r
4359     }\r
4360     if(y == -2) {\r
4361       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4362       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4363           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4364       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4365           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4366       }\r
4367       break;\r
4368     }\r
4369     DrawPosition(TRUE, NULL);\r
4370 \r
4371     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4372     switch (menuNr) {\r
4373     case 0:\r
4374       if (message == WM_MBUTTONDOWN) {\r
4375         buttonCount = 3;  /* even if system didn't think so */\r
4376         if (wParam & MK_SHIFT) \r
4377           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4378         else\r
4379           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4380       } else { /* message == WM_RBUTTONDOWN */\r
4381         /* Just have one menu, on the right button.  Windows users don't\r
4382            think to try the middle one, and sometimes other software steals\r
4383            it, or it doesn't really exist. */\r
4384         if(gameInfo.variant != VariantShogi)\r
4385             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4386         else\r
4387             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4388       }\r
4389       break;\r
4390     case 2:\r
4391       SetCapture(hwndMain);\r
4392       break;\r
4393     case 1:\r
4394       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4395       SetupDropMenu(hmenu);\r
4396       MenuPopup(hwnd, pt, hmenu, -1);\r
4397     default:\r
4398       break;\r
4399     }\r
4400     break;\r
4401   }\r
4402 \r
4403   recursive--;\r
4404 }\r
4405 \r
4406 /* Preprocess messages for buttons in main window */\r
4407 LRESULT CALLBACK\r
4408 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4409 {\r
4410   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4411   int i, dir;\r
4412 \r
4413   for (i=0; i<N_BUTTONS; i++) {\r
4414     if (buttonDesc[i].id == id) break;\r
4415   }\r
4416   if (i == N_BUTTONS) return 0;\r
4417   switch (message) {\r
4418   case WM_KEYDOWN:\r
4419     switch (wParam) {\r
4420     case VK_LEFT:\r
4421     case VK_RIGHT:\r
4422       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4423       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4424       return TRUE;\r
4425     }\r
4426     break;\r
4427   case WM_CHAR:\r
4428     switch (wParam) {\r
4429     case '\r':\r
4430       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4431       return TRUE;\r
4432     default:\r
4433       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4434         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4435         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4436         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4437         SetFocus(h);\r
4438         SendMessage(h, WM_CHAR, wParam, lParam);\r
4439         return TRUE;\r
4440       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4441         TypeInEvent((char)wParam);\r
4442       }\r
4443       break;\r
4444     }\r
4445     break;\r
4446   }\r
4447   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4448 }\r
4449 \r
4450 static int promoStyle;\r
4451 \r
4452 /* Process messages for Promotion dialog box */\r
4453 LRESULT CALLBACK\r
4454 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4455 {\r
4456   char promoChar;\r
4457 \r
4458   switch (message) {\r
4459   case WM_INITDIALOG: /* message: initialize dialog box */\r
4460     /* Center the dialog over the application window */\r
4461     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4462     Translate(hDlg, DLG_PromotionKing);\r
4463     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4464       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4465        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4466        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4467                SW_SHOW : SW_HIDE);\r
4468     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4469     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4470        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4471          PieceToChar(WhiteAngel) != '~') ||\r
4472         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4473          PieceToChar(BlackAngel) != '~')   ) ?\r
4474                SW_SHOW : SW_HIDE);\r
4475     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4476        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4477          PieceToChar(WhiteMarshall) != '~') ||\r
4478         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4479          PieceToChar(BlackMarshall) != '~')   ) ?\r
4480                SW_SHOW : SW_HIDE);\r
4481     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4482     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4483     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4484     if(promoStyle) {\r
4485         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4486         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4487         SetWindowText(hDlg, "Promote?");\r
4488     }\r
4489     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4490        gameInfo.variant == VariantSuper ?\r
4491                SW_SHOW : SW_HIDE);\r
4492     return TRUE;\r
4493 \r
4494   case WM_COMMAND: /* message: received a command */\r
4495     switch (LOWORD(wParam)) {\r
4496     case IDCANCEL:\r
4497       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4498       ClearHighlights();\r
4499       DrawPosition(FALSE, NULL);\r
4500       return TRUE;\r
4501     case PB_King:\r
4502       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4503       break;\r
4504     case PB_Queen:\r
4505       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4506       break;\r
4507     case PB_Rook:\r
4508       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4509       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4510       break;\r
4511     case PB_Bishop:\r
4512       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4513       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4514       break;\r
4515     case PB_Chancellor:\r
4516       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4517       break;\r
4518     case PB_Archbishop:\r
4519       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4520       break;\r
4521     case PB_Knight:\r
4522       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4523                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4524       break;\r
4525     default:\r
4526       return FALSE;\r
4527     }\r
4528     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4529     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4530     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4531     fromX = fromY = -1;\r
4532     if (!appData.highlightLastMove) {\r
4533       ClearHighlights();\r
4534       DrawPosition(FALSE, NULL);\r
4535     }\r
4536     return TRUE;\r
4537   }\r
4538   return FALSE;\r
4539 }\r
4540 \r
4541 /* Pop up promotion dialog */\r
4542 VOID\r
4543 PromotionPopup(HWND hwnd)\r
4544 {\r
4545   FARPROC lpProc;\r
4546 \r
4547   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4548   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4549     hwnd, (DLGPROC)lpProc);\r
4550   FreeProcInstance(lpProc);\r
4551 }\r
4552 \r
4553 void\r
4554 PromotionPopUp(char choice)\r
4555 {\r
4556   promoStyle = (choice == '+');\r
4557   DrawPosition(TRUE, NULL);\r
4558   PromotionPopup(hwndMain);\r
4559 }\r
4560 \r
4561 VOID\r
4562 LoadGameDialog(HWND hwnd, char* title)\r
4563 {\r
4564   UINT number = 0;\r
4565   FILE *f;\r
4566   char fileTitle[MSG_SIZ];\r
4567   f = OpenFileDialog(hwnd, "rb", "",\r
4568                      appData.oldSaveStyle ? "gam" : "pgn",\r
4569                      GAME_FILT,\r
4570                      title, &number, fileTitle, NULL);\r
4571   if (f != NULL) {\r
4572     cmailMsgLoaded = FALSE;\r
4573     if (number == 0) {\r
4574       int error = GameListBuild(f);\r
4575       if (error) {\r
4576         DisplayError(_("Cannot build game list"), error);\r
4577       } else if (!ListEmpty(&gameList) &&\r
4578                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4579         GameListPopUp(f, fileTitle);\r
4580         return;\r
4581       }\r
4582       GameListDestroy();\r
4583       number = 1;\r
4584     }\r
4585     LoadGame(f, number, fileTitle, FALSE);\r
4586   }\r
4587 }\r
4588 \r
4589 int get_term_width()\r
4590 {\r
4591     HDC hdc;\r
4592     TEXTMETRIC tm;\r
4593     RECT rc;\r
4594     HFONT hfont, hold_font;\r
4595     LOGFONT lf;\r
4596     HWND hText;\r
4597 \r
4598     if (hwndConsole)\r
4599         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4600     else\r
4601         return 79;\r
4602 \r
4603     // get the text metrics\r
4604     hdc = GetDC(hText);\r
4605     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4606     if (consoleCF.dwEffects & CFE_BOLD)\r
4607         lf.lfWeight = FW_BOLD;\r
4608     if (consoleCF.dwEffects & CFE_ITALIC)\r
4609         lf.lfItalic = TRUE;\r
4610     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4611         lf.lfStrikeOut = TRUE;\r
4612     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4613         lf.lfUnderline = TRUE;\r
4614     hfont = CreateFontIndirect(&lf);\r
4615     hold_font = SelectObject(hdc, hfont);\r
4616     GetTextMetrics(hdc, &tm);\r
4617     SelectObject(hdc, hold_font);\r
4618     DeleteObject(hfont);\r
4619     ReleaseDC(hText, hdc);\r
4620 \r
4621     // get the rectangle\r
4622     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4623 \r
4624     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4625 }\r
4626 \r
4627 void UpdateICSWidth(HWND hText)\r
4628 {\r
4629     LONG old_width, new_width;\r
4630 \r
4631     new_width = get_term_width(hText, FALSE);\r
4632     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4633     if (new_width != old_width)\r
4634     {\r
4635         ics_update_width(new_width);\r
4636         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4637     }\r
4638 }\r
4639 \r
4640 VOID\r
4641 ChangedConsoleFont()\r
4642 {\r
4643   CHARFORMAT cfmt;\r
4644   CHARRANGE tmpsel, sel;\r
4645   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4646   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4647   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4648   PARAFORMAT paraf;\r
4649 \r
4650   cfmt.cbSize = sizeof(CHARFORMAT);\r
4651   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4652     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4653                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4654   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4655    * size.  This was undocumented in the version of MSVC++ that I had\r
4656    * when I wrote the code, but is apparently documented now.\r
4657    */\r
4658   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4659   cfmt.bCharSet = f->lf.lfCharSet;\r
4660   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4661   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4662   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4663   /* Why are the following seemingly needed too? */\r
4664   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4665   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4666   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4667   tmpsel.cpMin = 0;\r
4668   tmpsel.cpMax = -1; /*999999?*/\r
4669   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4670   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4671   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4672    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4673    */\r
4674   paraf.cbSize = sizeof(paraf);\r
4675   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4676   paraf.dxStartIndent = 0;\r
4677   paraf.dxOffset = WRAP_INDENT;\r
4678   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4679   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4680   UpdateICSWidth(hText);\r
4681 }\r
4682 \r
4683 /*---------------------------------------------------------------------------*\\r
4684  *\r
4685  * Window Proc for main window\r
4686  *\r
4687 \*---------------------------------------------------------------------------*/\r
4688 \r
4689 /* Process messages for main window, etc. */\r
4690 LRESULT CALLBACK\r
4691 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4692 {\r
4693   FARPROC lpProc;\r
4694   int wmId, wmEvent;\r
4695   char *defName;\r
4696   FILE *f;\r
4697   UINT number;\r
4698   char fileTitle[MSG_SIZ];\r
4699   static SnapData sd;\r
4700   static int peek=0;\r
4701 \r
4702   switch (message) {\r
4703 \r
4704   case WM_PAINT: /* message: repaint portion of window */\r
4705     PaintProc(hwnd);\r
4706     break;\r
4707 \r
4708   case WM_ERASEBKGND:\r
4709     if (IsIconic(hwnd)) {\r
4710       /* Cheat; change the message */\r
4711       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4712     } else {\r
4713       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4714     }\r
4715     break;\r
4716 \r
4717   case WM_LBUTTONDOWN:\r
4718   case WM_MBUTTONDOWN:\r
4719   case WM_RBUTTONDOWN:\r
4720   case WM_LBUTTONUP:\r
4721   case WM_MBUTTONUP:\r
4722   case WM_RBUTTONUP:\r
4723   case WM_MOUSEMOVE:\r
4724   case WM_MOUSEWHEEL:\r
4725     MouseEvent(hwnd, message, wParam, lParam);\r
4726     break;\r
4727 \r
4728   case WM_KEYUP:\r
4729     if((char)wParam == '\b') {\r
4730       ForwardEvent(); peek = 0;\r
4731     }\r
4732 \r
4733     JAWS_KBUP_NAVIGATION\r
4734 \r
4735     break;\r
4736 \r
4737   case WM_KEYDOWN:\r
4738     if((char)wParam == '\b') {\r
4739       if(!peek) BackwardEvent(), peek = 1;\r
4740     }\r
4741 \r
4742     JAWS_KBDOWN_NAVIGATION\r
4743 \r
4744     break;\r
4745 \r
4746   case WM_CHAR:\r
4747     \r
4748     JAWS_ALT_INTERCEPT\r
4749 \r
4750     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4751         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4752         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4753         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4754         SetFocus(h);\r
4755         SendMessage(h, message, wParam, lParam);\r
4756     } else if(lParam != KF_REPEAT) {\r
4757         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4758                 TypeInEvent((char)wParam);\r
4759         } else if((char)wParam == 003) CopyGameToClipboard();\r
4760          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4761     }\r
4762 \r
4763     break;\r
4764 \r
4765   case WM_PALETTECHANGED:\r
4766     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4767       int nnew;\r
4768       HDC hdc = GetDC(hwndMain);\r
4769       SelectPalette(hdc, hPal, TRUE);\r
4770       nnew = RealizePalette(hdc);\r
4771       if (nnew > 0) {\r
4772         paletteChanged = TRUE;\r
4773 \r
4774         InvalidateRect(hwnd, &boardRect, FALSE);\r
4775       }\r
4776       ReleaseDC(hwnd, hdc);\r
4777     }\r
4778     break;\r
4779 \r
4780   case WM_QUERYNEWPALETTE:\r
4781     if (!appData.monoMode /*&& paletteChanged*/) {\r
4782       int nnew;\r
4783       HDC hdc = GetDC(hwndMain);\r
4784       paletteChanged = FALSE;\r
4785       SelectPalette(hdc, hPal, FALSE);\r
4786       nnew = RealizePalette(hdc);\r
4787       if (nnew > 0) {\r
4788         InvalidateRect(hwnd, &boardRect, FALSE);\r
4789       }\r
4790       ReleaseDC(hwnd, hdc);\r
4791       return TRUE;\r
4792     }\r
4793     return FALSE;\r
4794 \r
4795   case WM_COMMAND: /* message: command from application menu */\r
4796     wmId    = LOWORD(wParam);\r
4797     wmEvent = HIWORD(wParam);\r
4798 \r
4799     switch (wmId) {\r
4800     case IDM_NewGame:\r
4801       ResetGameEvent();\r
4802       SAY("new game enter a move to play against the computer with white");\r
4803       break;\r
4804 \r
4805     case IDM_NewGameFRC:\r
4806       if( NewGameFRC() == 0 ) {\r
4807         ResetGameEvent();\r
4808       }\r
4809       break;\r
4810 \r
4811     case IDM_NewVariant:\r
4812       NewVariantPopup(hwnd);\r
4813       break;\r
4814 \r
4815     case IDM_LoadGame:\r
4816       LoadGameDialog(hwnd, _("Load Game from File"));\r
4817       break;\r
4818 \r
4819     case IDM_LoadNextGame:\r
4820       ReloadGame(1);\r
4821       break;\r
4822 \r
4823     case IDM_LoadPrevGame:\r
4824       ReloadGame(-1);\r
4825       break;\r
4826 \r
4827     case IDM_ReloadGame:\r
4828       ReloadGame(0);\r
4829       break;\r
4830 \r
4831     case IDM_LoadPosition:\r
4832       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4833         Reset(FALSE, TRUE);\r
4834       }\r
4835       number = 1;\r
4836       f = OpenFileDialog(hwnd, "rb", "",\r
4837                          appData.oldSaveStyle ? "pos" : "fen",\r
4838                          POSITION_FILT,\r
4839                          _("Load Position from File"), &number, fileTitle, NULL);\r
4840       if (f != NULL) {\r
4841         LoadPosition(f, number, fileTitle);\r
4842       }\r
4843       break;\r
4844 \r
4845     case IDM_LoadNextPosition:\r
4846       ReloadPosition(1);\r
4847       break;\r
4848 \r
4849     case IDM_LoadPrevPosition:\r
4850       ReloadPosition(-1);\r
4851       break;\r
4852 \r
4853     case IDM_ReloadPosition:\r
4854       ReloadPosition(0);\r
4855       break;\r
4856 \r
4857     case IDM_SaveGame:\r
4858       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4859       f = OpenFileDialog(hwnd, "a", defName,\r
4860                          appData.oldSaveStyle ? "gam" : "pgn",\r
4861                          GAME_FILT,\r
4862                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4863       if (f != NULL) {\r
4864         SaveGame(f, 0, "");\r
4865       }\r
4866       break;\r
4867 \r
4868     case IDM_SavePosition:\r
4869       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4870       f = OpenFileDialog(hwnd, "a", defName,\r
4871                          appData.oldSaveStyle ? "pos" : "fen",\r
4872                          POSITION_FILT,\r
4873                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4874       if (f != NULL) {\r
4875         SavePosition(f, 0, "");\r
4876       }\r
4877       break;\r
4878 \r
4879     case IDM_SaveDiagram:\r
4880       defName = "diagram";\r
4881       f = OpenFileDialog(hwnd, "wb", defName,\r
4882                          "bmp",\r
4883                          DIAGRAM_FILT,\r
4884                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4885       if (f != NULL) {\r
4886         SaveDiagram(f);\r
4887       }\r
4888       break;\r
4889 \r
4890     case IDM_CreateBook:\r
4891       CreateBookEvent();\r
4892       break;\r
4893 \r
4894     case IDM_CopyGame:\r
4895       CopyGameToClipboard();\r
4896       break;\r
4897 \r
4898     case IDM_PasteGame:\r
4899       PasteGameFromClipboard();\r
4900       break;\r
4901 \r
4902     case IDM_CopyGameListToClipboard:\r
4903       CopyGameListToClipboard();\r
4904       break;\r
4905 \r
4906     /* [AS] Autodetect FEN or PGN data */\r
4907     case IDM_PasteAny:\r
4908       PasteGameOrFENFromClipboard();\r
4909       break;\r
4910 \r
4911     /* [AS] Move history */\r
4912     case IDM_ShowMoveHistory:\r
4913         if( MoveHistoryIsUp() ) {\r
4914             MoveHistoryPopDown();\r
4915         }\r
4916         else {\r
4917             MoveHistoryPopUp();\r
4918         }\r
4919         break;\r
4920 \r
4921     /* [AS] Eval graph */\r
4922     case IDM_ShowEvalGraph:\r
4923         if( EvalGraphIsUp() ) {\r
4924             EvalGraphPopDown();\r
4925         }\r
4926         else {\r
4927             EvalGraphPopUp();\r
4928             SetFocus(hwndMain);\r
4929         }\r
4930         break;\r
4931 \r
4932     /* [AS] Engine output */\r
4933     case IDM_ShowEngineOutput:\r
4934         if( EngineOutputIsUp() ) {\r
4935             EngineOutputPopDown();\r
4936         }\r
4937         else {\r
4938             EngineOutputPopUp();\r
4939         }\r
4940         break;\r
4941 \r
4942     /* [AS] User adjudication */\r
4943     case IDM_UserAdjudication_White:\r
4944         UserAdjudicationEvent( +1 );\r
4945         break;\r
4946 \r
4947     case IDM_UserAdjudication_Black:\r
4948         UserAdjudicationEvent( -1 );\r
4949         break;\r
4950 \r
4951     case IDM_UserAdjudication_Draw:\r
4952         UserAdjudicationEvent( 0 );\r
4953         break;\r
4954 \r
4955     /* [AS] Game list options dialog */\r
4956     case IDM_GameListOptions:\r
4957       GameListOptions();\r
4958       break;\r
4959 \r
4960     case IDM_NewChat:\r
4961       ChatPopUp(NULL);\r
4962       break;\r
4963 \r
4964     case IDM_CopyPosition:\r
4965       CopyFENToClipboard();\r
4966       break;\r
4967 \r
4968     case IDM_PastePosition:\r
4969       PasteFENFromClipboard();\r
4970       break;\r
4971 \r
4972     case IDM_MailMove:\r
4973       MailMoveEvent();\r
4974       break;\r
4975 \r
4976     case IDM_ReloadCMailMsg:\r
4977       Reset(TRUE, TRUE);\r
4978       ReloadCmailMsgEvent(FALSE);\r
4979       break;\r
4980 \r
4981     case IDM_Minimize:\r
4982       ShowWindow(hwnd, SW_MINIMIZE);\r
4983       break;\r
4984 \r
4985     case IDM_Exit:\r
4986       ExitEvent(0);\r
4987       break;\r
4988 \r
4989     case IDM_MachineWhite:\r
4990       MachineWhiteEvent();\r
4991       /*\r
4992        * refresh the tags dialog only if it's visible\r
4993        */\r
4994       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4995           char *tags;\r
4996           tags = PGNTags(&gameInfo);\r
4997           TagsPopUp(tags, CmailMsg());\r
4998           free(tags);\r
4999       }\r
5000       SAY("computer starts playing white");\r
5001       break;\r
5002 \r
5003     case IDM_MachineBlack:\r
5004       MachineBlackEvent();\r
5005       /*\r
5006        * refresh the tags dialog only if it's visible\r
5007        */\r
5008       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5009           char *tags;\r
5010           tags = PGNTags(&gameInfo);\r
5011           TagsPopUp(tags, CmailMsg());\r
5012           free(tags);\r
5013       }\r
5014       SAY("computer starts playing black");\r
5015       break;\r
5016 \r
5017     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5018       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5019       break;\r
5020 \r
5021     case IDM_TwoMachines:\r
5022       TwoMachinesEvent();\r
5023       /*\r
5024        * refresh the tags dialog only if it's visible\r
5025        */\r
5026       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5027           char *tags;\r
5028           tags = PGNTags(&gameInfo);\r
5029           TagsPopUp(tags, CmailMsg());\r
5030           free(tags);\r
5031       }\r
5032       SAY("computer starts playing both sides");\r
5033       break;\r
5034 \r
5035     case IDM_AnalysisMode:\r
5036       if(AnalyzeModeEvent()) {\r
5037         SAY("analyzing current position");\r
5038       }\r
5039       break;\r
5040 \r
5041     case IDM_AnalyzeFile:\r
5042       AnalyzeFileEvent();\r
5043       break;\r
5044 \r
5045     case IDM_IcsClient:\r
5046       IcsClientEvent();\r
5047       break;\r
5048 \r
5049     case IDM_EditGame:\r
5050     case IDM_EditGame2:\r
5051       EditGameEvent();\r
5052       SAY("edit game");\r
5053       break;\r
5054 \r
5055     case IDM_EditPosition:\r
5056     case IDM_EditPosition2:\r
5057       EditPositionEvent();\r
5058       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5059       break;\r
5060 \r
5061     case IDM_Training:\r
5062       TrainingEvent();\r
5063       break;\r
5064 \r
5065     case IDM_ShowGameList:\r
5066       ShowGameListProc();\r
5067       break;\r
5068 \r
5069     case IDM_EditProgs1:\r
5070       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5071       break;\r
5072 \r
5073     case IDM_LoadProg1:\r
5074      LoadEnginePopUp(hwndMain, 0);\r
5075       break;\r
5076 \r
5077     case IDM_LoadProg2:\r
5078      LoadEnginePopUp(hwndMain, 1);\r
5079       break;\r
5080 \r
5081     case IDM_EditServers:\r
5082       EditTagsPopUp(icsNames, &icsNames);\r
5083       break;\r
5084 \r
5085     case IDM_EditTags:\r
5086     case IDM_Tags:\r
5087       EditTagsProc();\r
5088       break;\r
5089 \r
5090     case IDM_EditBook:\r
5091       EditBookEvent();\r
5092       break;\r
5093 \r
5094     case IDM_EditComment:\r
5095     case IDM_Comment:\r
5096       if (commentUp && editComment) {\r
5097         CommentPopDown();\r
5098       } else {\r
5099         EditCommentEvent();\r
5100       }\r
5101       break;\r
5102 \r
5103     case IDM_Pause:\r
5104       PauseEvent();\r
5105       break;\r
5106 \r
5107     case IDM_Accept:\r
5108       AcceptEvent();\r
5109       break;\r
5110 \r
5111     case IDM_Decline:\r
5112       DeclineEvent();\r
5113       break;\r
5114 \r
5115     case IDM_Rematch:\r
5116 \r
5117       RematchEvent();\r
5118       break;\r
5119 \r
5120     case IDM_CallFlag:\r
5121       CallFlagEvent();\r
5122       break;\r
5123 \r
5124     case IDM_Draw:\r
5125       DrawEvent();\r
5126       break;\r
5127 \r
5128     case IDM_Adjourn:\r
5129       AdjournEvent();\r
5130       break;\r
5131 \r
5132     case IDM_Abort:\r
5133       AbortEvent();\r
5134       break;\r
5135 \r
5136     case IDM_Resign:\r
5137       ResignEvent();\r
5138       break;\r
5139 \r
5140     case IDM_StopObserving:\r
5141       StopObservingEvent();\r
5142       break;\r
5143 \r
5144     case IDM_StopExamining:\r
5145       StopExaminingEvent();\r
5146       break;\r
5147 \r
5148     case IDM_Upload:\r
5149       UploadGameEvent();\r
5150       break;\r
5151 \r
5152     case IDM_TypeInMove:\r
5153       TypeInEvent('\000');\r
5154       break;\r
5155 \r
5156     case IDM_TypeInName:\r
5157       PopUpNameDialog('\000');\r
5158       break;\r
5159 \r
5160     case IDM_Backward:\r
5161       BackwardEvent();\r
5162       SetFocus(hwndMain);\r
5163       break;\r
5164 \r
5165     JAWS_MENU_ITEMS\r
5166 \r
5167     case IDM_Forward:\r
5168       ForwardEvent();\r
5169       SetFocus(hwndMain);\r
5170       break;\r
5171 \r
5172     case IDM_ToStart:\r
5173       ToStartEvent();\r
5174       SetFocus(hwndMain);\r
5175       break;\r
5176 \r
5177     case IDM_ToEnd:\r
5178       ToEndEvent();\r
5179       SetFocus(hwndMain);\r
5180       break;\r
5181 \r
5182     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5183     case OPT_GameListPrev:\r
5184       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5185       break;\r
5186 \r
5187     case IDM_Revert:\r
5188       RevertEvent(FALSE);\r
5189       break;\r
5190 \r
5191     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5192       RevertEvent(TRUE);\r
5193       break;\r
5194 \r
5195     case IDM_TruncateGame:\r
5196       TruncateGameEvent();\r
5197       break;\r
5198 \r
5199     case IDM_MoveNow:\r
5200       MoveNowEvent();\r
5201       break;\r
5202 \r
5203     case IDM_RetractMove:\r
5204       RetractMoveEvent();\r
5205       break;\r
5206 \r
5207     case IDM_FlipView:\r
5208       flipView = !flipView;\r
5209       DrawPosition(FALSE, NULL);\r
5210       break;\r
5211 \r
5212     case IDM_FlipClock:\r
5213       flipClock = !flipClock;\r
5214       DisplayBothClocks();\r
5215       DisplayLogos();\r
5216       break;\r
5217 \r
5218     case IDM_MuteSounds:\r
5219       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5220       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5221                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5222       break;\r
5223 \r
5224     case IDM_GeneralOptions:\r
5225       GeneralOptionsPopup(hwnd);\r
5226       DrawPosition(TRUE, NULL);\r
5227       break;\r
5228 \r
5229     case IDM_BoardOptions:\r
5230       BoardOptionsPopup(hwnd);\r
5231       break;\r
5232 \r
5233     case IDM_ThemeOptions:\r
5234       ThemeOptionsPopup(hwnd);\r
5235       break;\r
5236 \r
5237     case IDM_EnginePlayOptions:\r
5238       EnginePlayOptionsPopup(hwnd);\r
5239       break;\r
5240 \r
5241     case IDM_Engine1Options:\r
5242       EngineOptionsPopup(hwnd, &first);\r
5243       break;\r
5244 \r
5245     case IDM_Engine2Options:\r
5246       savedHwnd = hwnd;\r
5247       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5248       EngineOptionsPopup(hwnd, &second);\r
5249       break;\r
5250 \r
5251     case IDM_OptionsUCI:\r
5252       UciOptionsPopup(hwnd);\r
5253       break;\r
5254 \r
5255     case IDM_Tourney:\r
5256       TourneyPopup(hwnd);\r
5257       break;\r
5258 \r
5259     case IDM_IcsOptions:\r
5260       IcsOptionsPopup(hwnd);\r
5261       break;\r
5262 \r
5263     case IDM_Fonts:\r
5264       FontsOptionsPopup(hwnd);\r
5265       break;\r
5266 \r
5267     case IDM_Sounds:\r
5268       SoundOptionsPopup(hwnd);\r
5269       break;\r
5270 \r
5271     case IDM_CommPort:\r
5272       CommPortOptionsPopup(hwnd);\r
5273       break;\r
5274 \r
5275     case IDM_LoadOptions:\r
5276       LoadOptionsPopup(hwnd);\r
5277       break;\r
5278 \r
5279     case IDM_SaveOptions:\r
5280       SaveOptionsPopup(hwnd);\r
5281       break;\r
5282 \r
5283     case IDM_TimeControl:\r
5284       TimeControlOptionsPopup(hwnd);\r
5285       break;\r
5286 \r
5287     case IDM_SaveSettings:\r
5288       SaveSettings(settingsFileName);\r
5289       break;\r
5290 \r
5291     case IDM_SaveSettingsOnExit:\r
5292       saveSettingsOnExit = !saveSettingsOnExit;\r
5293       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5294                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5295                                          MF_CHECKED : MF_UNCHECKED));\r
5296       break;\r
5297 \r
5298     case IDM_Hint:\r
5299       HintEvent();\r
5300       break;\r
5301 \r
5302     case IDM_Book:\r
5303       BookEvent();\r
5304       break;\r
5305 \r
5306     case IDM_AboutGame:\r
5307       AboutGameEvent();\r
5308       break;\r
5309 \r
5310     case IDM_Debug:\r
5311       appData.debugMode = !appData.debugMode;\r
5312       if (appData.debugMode) {\r
5313         char dir[MSG_SIZ];\r
5314         GetCurrentDirectory(MSG_SIZ, dir);\r
5315         SetCurrentDirectory(installDir);\r
5316         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5317         SetCurrentDirectory(dir);\r
5318         setbuf(debugFP, NULL);\r
5319       } else {\r
5320         fclose(debugFP);\r
5321         debugFP = NULL;\r
5322       }\r
5323       break;\r
5324 \r
5325     case IDM_HELPCONTENTS:\r
5326       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5327           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5328           MessageBox (GetFocus(),\r
5329                     _("Unable to activate help"),\r
5330                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5331       }\r
5332       break;\r
5333 \r
5334     case IDM_HELPSEARCH:\r
5335         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5336             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5337         MessageBox (GetFocus(),\r
5338                     _("Unable to activate help"),\r
5339                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5340       }\r
5341       break;\r
5342 \r
5343     case IDM_HELPHELP:\r
5344       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5345         MessageBox (GetFocus(),\r
5346                     _("Unable to activate help"),\r
5347                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5348       }\r
5349       break;\r
5350 \r
5351     case IDM_ABOUT:\r
5352       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5353       DialogBox(hInst, \r
5354         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5355         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5356       FreeProcInstance(lpProc);\r
5357       break;\r
5358 \r
5359     case IDM_DirectCommand1:\r
5360       AskQuestionEvent(_("Direct Command"),\r
5361                        _("Send to chess program:"), "", "1");\r
5362       break;\r
5363     case IDM_DirectCommand2:\r
5364       AskQuestionEvent(_("Direct Command"),\r
5365                        _("Send to second chess program:"), "", "2");\r
5366       break;\r
5367 \r
5368     case EP_WhitePawn:\r
5369       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5370       fromX = fromY = -1;\r
5371       break;\r
5372 \r
5373     case EP_WhiteKnight:\r
5374       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5375       fromX = fromY = -1;\r
5376       break;\r
5377 \r
5378     case EP_WhiteBishop:\r
5379       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5380       fromX = fromY = -1;\r
5381       break;\r
5382 \r
5383     case EP_WhiteRook:\r
5384       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5385       fromX = fromY = -1;\r
5386       break;\r
5387 \r
5388     case EP_WhiteQueen:\r
5389       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5390       fromX = fromY = -1;\r
5391       break;\r
5392 \r
5393     case EP_WhiteFerz:\r
5394       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5395       fromX = fromY = -1;\r
5396       break;\r
5397 \r
5398     case EP_WhiteWazir:\r
5399       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5400       fromX = fromY = -1;\r
5401       break;\r
5402 \r
5403     case EP_WhiteAlfil:\r
5404       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5405       fromX = fromY = -1;\r
5406       break;\r
5407 \r
5408     case EP_WhiteCannon:\r
5409       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5410       fromX = fromY = -1;\r
5411       break;\r
5412 \r
5413     case EP_WhiteCardinal:\r
5414       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5415       fromX = fromY = -1;\r
5416       break;\r
5417 \r
5418     case EP_WhiteMarshall:\r
5419       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5420       fromX = fromY = -1;\r
5421       break;\r
5422 \r
5423     case EP_WhiteKing:\r
5424       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5425       fromX = fromY = -1;\r
5426       break;\r
5427 \r
5428     case EP_BlackPawn:\r
5429       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5430       fromX = fromY = -1;\r
5431       break;\r
5432 \r
5433     case EP_BlackKnight:\r
5434       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5435       fromX = fromY = -1;\r
5436       break;\r
5437 \r
5438     case EP_BlackBishop:\r
5439       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5440       fromX = fromY = -1;\r
5441       break;\r
5442 \r
5443     case EP_BlackRook:\r
5444       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5445       fromX = fromY = -1;\r
5446       break;\r
5447 \r
5448     case EP_BlackQueen:\r
5449       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5450       fromX = fromY = -1;\r
5451       break;\r
5452 \r
5453     case EP_BlackFerz:\r
5454       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5455       fromX = fromY = -1;\r
5456       break;\r
5457 \r
5458     case EP_BlackWazir:\r
5459       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5460       fromX = fromY = -1;\r
5461       break;\r
5462 \r
5463     case EP_BlackAlfil:\r
5464       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5465       fromX = fromY = -1;\r
5466       break;\r
5467 \r
5468     case EP_BlackCannon:\r
5469       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5470       fromX = fromY = -1;\r
5471       break;\r
5472 \r
5473     case EP_BlackCardinal:\r
5474       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5475       fromX = fromY = -1;\r
5476       break;\r
5477 \r
5478     case EP_BlackMarshall:\r
5479       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5480       fromX = fromY = -1;\r
5481       break;\r
5482 \r
5483     case EP_BlackKing:\r
5484       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5485       fromX = fromY = -1;\r
5486       break;\r
5487 \r
5488     case EP_EmptySquare:\r
5489       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5490       fromX = fromY = -1;\r
5491       break;\r
5492 \r
5493     case EP_ClearBoard:\r
5494       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5495       fromX = fromY = -1;\r
5496       break;\r
5497 \r
5498     case EP_White:\r
5499       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5500       fromX = fromY = -1;\r
5501       break;\r
5502 \r
5503     case EP_Black:\r
5504       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5505       fromX = fromY = -1;\r
5506       break;\r
5507 \r
5508     case EP_Promote:\r
5509       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5510       fromX = fromY = -1;\r
5511       break;\r
5512 \r
5513     case EP_Demote:\r
5514       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5515       fromX = fromY = -1;\r
5516       break;\r
5517 \r
5518     case DP_Pawn:\r
5519       DropMenuEvent(WhitePawn, fromX, fromY);\r
5520       fromX = fromY = -1;\r
5521       break;\r
5522 \r
5523     case DP_Knight:\r
5524       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5525       fromX = fromY = -1;\r
5526       break;\r
5527 \r
5528     case DP_Bishop:\r
5529       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5530       fromX = fromY = -1;\r
5531       break;\r
5532 \r
5533     case DP_Rook:\r
5534       DropMenuEvent(WhiteRook, fromX, fromY);\r
5535       fromX = fromY = -1;\r
5536       break;\r
5537 \r
5538     case DP_Queen:\r
5539       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5540       fromX = fromY = -1;\r
5541       break;\r
5542 \r
5543     case IDM_English:\r
5544       barbaric = 0; appData.language = "";\r
5545       TranslateMenus(0);\r
5546       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5547       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5548       lastChecked = wmId;\r
5549       break;\r
5550 \r
5551     default:\r
5552       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5553           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5554       else\r
5555       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5556           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5557           TranslateMenus(0);\r
5558           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5559           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5560           lastChecked = wmId;\r
5561           break;\r
5562       }\r
5563       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5564     }\r
5565     break;\r
5566 \r
5567   case WM_TIMER:\r
5568     switch (wParam) {\r
5569     case CLOCK_TIMER_ID:\r
5570       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5571       clockTimerEvent = 0;\r
5572       DecrementClocks(); /* call into back end */\r
5573       break;\r
5574     case LOAD_GAME_TIMER_ID:\r
5575       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5576       loadGameTimerEvent = 0;\r
5577       AutoPlayGameLoop(); /* call into back end */\r
5578       break;\r
5579     case ANALYSIS_TIMER_ID:\r
5580       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5581                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5582         AnalysisPeriodicEvent(0);\r
5583       } else {\r
5584         KillTimer(hwnd, analysisTimerEvent);\r
5585         analysisTimerEvent = 0;\r
5586       }\r
5587       break;\r
5588     case DELAYED_TIMER_ID:\r
5589       KillTimer(hwnd, delayedTimerEvent);\r
5590       delayedTimerEvent = 0;\r
5591       delayedTimerCallback();\r
5592       break;\r
5593     }\r
5594     break;\r
5595 \r
5596   case WM_USER_Input:\r
5597     InputEvent(hwnd, message, wParam, lParam);\r
5598     break;\r
5599 \r
5600   /* [AS] Also move "attached" child windows */\r
5601   case WM_WINDOWPOSCHANGING:\r
5602 \r
5603     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5604         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5605 \r
5606         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5607             /* Window is moving */\r
5608             RECT rcMain;\r
5609 \r
5610 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5611             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5612             rcMain.right  = wpMain.x + wpMain.width;\r
5613             rcMain.top    = wpMain.y;\r
5614             rcMain.bottom = wpMain.y + wpMain.height;\r
5615             \r
5616             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5617             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5618             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5619             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5620             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5621             wpMain.x = lpwp->x;\r
5622             wpMain.y = lpwp->y;\r
5623         }\r
5624     }\r
5625     break;\r
5626 \r
5627   /* [AS] Snapping */\r
5628   case WM_ENTERSIZEMOVE:\r
5629     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5630     if (hwnd == hwndMain) {\r
5631       doingSizing = TRUE;\r
5632       lastSizing = 0;\r
5633     }\r
5634     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5635     break;\r
5636 \r
5637   case WM_SIZING:\r
5638     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5639     if (hwnd == hwndMain) {\r
5640       lastSizing = wParam;\r
5641     }\r
5642     break;\r
5643 \r
5644   case WM_MOVING:\r
5645     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5646       return OnMoving( &sd, hwnd, wParam, lParam );\r
5647 \r
5648   case WM_EXITSIZEMOVE:\r
5649     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5650     if (hwnd == hwndMain) {\r
5651       RECT client;\r
5652       doingSizing = FALSE;\r
5653       InvalidateRect(hwnd, &boardRect, FALSE);\r
5654       GetClientRect(hwnd, &client);\r
5655       ResizeBoard(client.right, client.bottom, lastSizing);\r
5656       lastSizing = 0;\r
5657       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5658     }\r
5659     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5660     break;\r
5661 \r
5662   case WM_DESTROY: /* message: window being destroyed */\r
5663     PostQuitMessage(0);\r
5664     break;\r
5665 \r
5666   case WM_CLOSE:\r
5667     if (hwnd == hwndMain) {\r
5668       ExitEvent(0);\r
5669     }\r
5670     break;\r
5671 \r
5672   default:      /* Passes it on if unprocessed */\r
5673     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5674   }\r
5675   return 0;\r
5676 }\r
5677 \r
5678 /*---------------------------------------------------------------------------*\\r
5679  *\r
5680  * Misc utility routines\r
5681  *\r
5682 \*---------------------------------------------------------------------------*/\r
5683 \r
5684 /*\r
5685  * Decent random number generator, at least not as bad as Windows\r
5686  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5687  */\r
5688 unsigned int randstate;\r
5689 \r
5690 int\r
5691 myrandom(void)\r
5692 {\r
5693   randstate = randstate * 1664525 + 1013904223;\r
5694   return (int) randstate & 0x7fffffff;\r
5695 }\r
5696 \r
5697 void\r
5698 mysrandom(unsigned int seed)\r
5699 {\r
5700   randstate = seed;\r
5701 }\r
5702 \r
5703 \r
5704 /* \r
5705  * returns TRUE if user selects a different color, FALSE otherwise \r
5706  */\r
5707 \r
5708 BOOL\r
5709 ChangeColor(HWND hwnd, COLORREF *which)\r
5710 {\r
5711   static BOOL firstTime = TRUE;\r
5712   static DWORD customColors[16];\r
5713   CHOOSECOLOR cc;\r
5714   COLORREF newcolor;\r
5715   int i;\r
5716   ColorClass ccl;\r
5717 \r
5718   if (firstTime) {\r
5719     /* Make initial colors in use available as custom colors */\r
5720     /* Should we put the compiled-in defaults here instead? */\r
5721     i = 0;\r
5722     customColors[i++] = lightSquareColor & 0xffffff;\r
5723     customColors[i++] = darkSquareColor & 0xffffff;\r
5724     customColors[i++] = whitePieceColor & 0xffffff;\r
5725     customColors[i++] = blackPieceColor & 0xffffff;\r
5726     customColors[i++] = highlightSquareColor & 0xffffff;\r
5727     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5728 \r
5729     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5730       customColors[i++] = textAttribs[ccl].color;\r
5731     }\r
5732     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5733     firstTime = FALSE;\r
5734   }\r
5735 \r
5736   cc.lStructSize = sizeof(cc);\r
5737   cc.hwndOwner = hwnd;\r
5738   cc.hInstance = NULL;\r
5739   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5740   cc.lpCustColors = (LPDWORD) customColors;\r
5741   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5742 \r
5743   if (!ChooseColor(&cc)) return FALSE;\r
5744 \r
5745   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5746   if (newcolor == *which) return FALSE;\r
5747   *which = newcolor;\r
5748   return TRUE;\r
5749 \r
5750   /*\r
5751   InitDrawingColors();\r
5752   InvalidateRect(hwnd, &boardRect, FALSE);\r
5753   */\r
5754 }\r
5755 \r
5756 BOOLEAN\r
5757 MyLoadSound(MySound *ms)\r
5758 {\r
5759   BOOL ok = FALSE;\r
5760   struct stat st;\r
5761   FILE *f;\r
5762 \r
5763   if (ms->data && ms->flag) free(ms->data);\r
5764   ms->data = NULL;\r
5765 \r
5766   switch (ms->name[0]) {\r
5767   case NULLCHAR:\r
5768     /* Silence */\r
5769     ok = TRUE;\r
5770     break;\r
5771   case '$':\r
5772     /* System sound from Control Panel.  Don't preload here. */\r
5773     ok = TRUE;\r
5774     break;\r
5775   case '!':\r
5776     if (ms->name[1] == NULLCHAR) {\r
5777       /* "!" alone = silence */\r
5778       ok = TRUE;\r
5779     } else {\r
5780       /* Builtin wave resource.  Error if not found. */\r
5781       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5782       if (h == NULL) break;\r
5783       ms->data = (void *)LoadResource(hInst, h);\r
5784       ms->flag = 0; // not maloced, so cannot be freed!\r
5785       if (h == NULL) break;\r
5786       ok = TRUE;\r
5787     }\r
5788     break;\r
5789   default:\r
5790     /* .wav file.  Error if not found. */\r
5791     f = fopen(ms->name, "rb");\r
5792     if (f == NULL) break;\r
5793     if (fstat(fileno(f), &st) < 0) break;\r
5794     ms->data = malloc(st.st_size);\r
5795     ms->flag = 1;\r
5796     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5797     fclose(f);\r
5798     ok = TRUE;\r
5799     break;\r
5800   }\r
5801   if (!ok) {\r
5802     char buf[MSG_SIZ];\r
5803       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5804     DisplayError(buf, GetLastError());\r
5805   }\r
5806   return ok;\r
5807 }\r
5808 \r
5809 BOOLEAN\r
5810 MyPlaySound(MySound *ms)\r
5811 {\r
5812   BOOLEAN ok = FALSE;\r
5813 \r
5814   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5815   switch (ms->name[0]) {\r
5816   case NULLCHAR:\r
5817         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5818     /* Silence */\r
5819     ok = TRUE;\r
5820     break;\r
5821   case '$':\r
5822     /* System sound from Control Panel (deprecated feature).\r
5823        "$" alone or an unset sound name gets default beep (still in use). */\r
5824     if (ms->name[1]) {\r
5825       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5826     }\r
5827     if (!ok) ok = MessageBeep(MB_OK);\r
5828     break; \r
5829   case '!':\r
5830     /* Builtin wave resource, or "!" alone for silence */\r
5831     if (ms->name[1]) {\r
5832       if (ms->data == NULL) return FALSE;\r
5833       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5834     } else {\r
5835       ok = TRUE;\r
5836     }\r
5837     break;\r
5838   default:\r
5839     /* .wav file.  Error if not found. */\r
5840     if (ms->data == NULL) return FALSE;\r
5841     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5842     break;\r
5843   }\r
5844   /* Don't print an error: this can happen innocently if the sound driver\r
5845      is busy; for instance, if another instance of WinBoard is playing\r
5846      a sound at about the same time. */\r
5847   return ok;\r
5848 }\r
5849 \r
5850 \r
5851 LRESULT CALLBACK\r
5852 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5853 {\r
5854   BOOL ok;\r
5855   OPENFILENAME *ofn;\r
5856   static UINT *number; /* gross that this is static */\r
5857 \r
5858   switch (message) {\r
5859   case WM_INITDIALOG: /* message: initialize dialog box */\r
5860     /* Center the dialog over the application window */\r
5861     ofn = (OPENFILENAME *) lParam;\r
5862     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5863       number = (UINT *) ofn->lCustData;\r
5864       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5865     } else {\r
5866       number = NULL;\r
5867     }\r
5868     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5869     Translate(hDlg, 1536);\r
5870     return FALSE;  /* Allow for further processing */\r
5871 \r
5872   case WM_COMMAND:\r
5873     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5874       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5875     }\r
5876     return FALSE;  /* Allow for further processing */\r
5877   }\r
5878   return FALSE;\r
5879 }\r
5880 \r
5881 UINT APIENTRY\r
5882 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5883 {\r
5884   static UINT *number;\r
5885   OPENFILENAME *ofname;\r
5886   OFNOTIFY *ofnot;\r
5887   switch (uiMsg) {\r
5888   case WM_INITDIALOG:\r
5889     Translate(hdlg, DLG_IndexNumber);\r
5890     ofname = (OPENFILENAME *)lParam;\r
5891     number = (UINT *)(ofname->lCustData);\r
5892     break;\r
5893   case WM_NOTIFY:\r
5894     ofnot = (OFNOTIFY *)lParam;\r
5895     if (ofnot->hdr.code == CDN_FILEOK) {\r
5896       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5897     }\r
5898     break;\r
5899   }\r
5900   return 0;\r
5901 }\r
5902 \r
5903 \r
5904 FILE *\r
5905 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5906                char *nameFilt, char *dlgTitle, UINT *number,\r
5907                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5908 {\r
5909   OPENFILENAME openFileName;\r
5910   char buf1[MSG_SIZ];\r
5911   FILE *f;\r
5912 \r
5913   if (fileName == NULL) fileName = buf1;\r
5914   if (defName == NULL) {\r
5915     safeStrCpy(fileName, "*.", 3 );\r
5916     strcat(fileName, defExt);\r
5917   } else {\r
5918     safeStrCpy(fileName, defName, MSG_SIZ );\r
5919   }\r
5920     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5921   if (number) *number = 0;\r
5922 \r
5923   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5924   openFileName.hwndOwner         = hwnd;\r
5925   openFileName.hInstance         = (HANDLE) hInst;\r
5926   openFileName.lpstrFilter       = nameFilt;\r
5927   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5928   openFileName.nMaxCustFilter    = 0L;\r
5929   openFileName.nFilterIndex      = 1L;\r
5930   openFileName.lpstrFile         = fileName;\r
5931   openFileName.nMaxFile          = MSG_SIZ;\r
5932   openFileName.lpstrFileTitle    = fileTitle;\r
5933   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5934   openFileName.lpstrInitialDir   = NULL;\r
5935   openFileName.lpstrTitle        = dlgTitle;\r
5936   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5937     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5938     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5939     | (oldDialog ? 0 : OFN_EXPLORER);\r
5940   openFileName.nFileOffset       = 0;\r
5941   openFileName.nFileExtension    = 0;\r
5942   openFileName.lpstrDefExt       = defExt;\r
5943   openFileName.lCustData         = (LONG) number;\r
5944   openFileName.lpfnHook          = oldDialog ?\r
5945     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5946   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5947 \r
5948   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5949                         GetOpenFileName(&openFileName)) {\r
5950     /* open the file */\r
5951     f = fopen(openFileName.lpstrFile, write);\r
5952     if (f == NULL) {\r
5953       MessageBox(hwnd, _("File open failed"), NULL,\r
5954                  MB_OK|MB_ICONEXCLAMATION);\r
5955       return NULL;\r
5956     }\r
5957   } else {\r
5958     int err = CommDlgExtendedError();\r
5959     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5960     return FALSE;\r
5961   }\r
5962   return f;\r
5963 }\r
5964 \r
5965 \r
5966 \r
5967 VOID APIENTRY\r
5968 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5969 {\r
5970   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5971 \r
5972   /*\r
5973    * Get the first pop-up menu in the menu template. This is the\r
5974    * menu that TrackPopupMenu displays.\r
5975    */\r
5976   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5977   TranslateOneMenu(10, hmenuTrackPopup);\r
5978 \r
5979   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5980 \r
5981   /*\r
5982    * TrackPopup uses screen coordinates, so convert the\r
5983    * coordinates of the mouse click to screen coordinates.\r
5984    */\r
5985   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5986 \r
5987   /* Draw and track the floating pop-up menu. */\r
5988   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5989                  pt.x, pt.y, 0, hwnd, NULL);\r
5990 \r
5991   /* Destroy the menu.*/\r
5992   DestroyMenu(hmenu);\r
5993 }\r
5994    \r
5995 typedef struct {\r
5996   HWND hDlg, hText;\r
5997   int sizeX, sizeY, newSizeX, newSizeY;\r
5998   HDWP hdwp;\r
5999 } ResizeEditPlusButtonsClosure;\r
6000 \r
6001 BOOL CALLBACK\r
6002 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6003 {\r
6004   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6005   RECT rect;\r
6006   POINT pt;\r
6007 \r
6008   if (hChild == cl->hText) return TRUE;\r
6009   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6010   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6011   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6012   ScreenToClient(cl->hDlg, &pt);\r
6013   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6014     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6015   return TRUE;\r
6016 }\r
6017 \r
6018 /* Resize a dialog that has a (rich) edit field filling most of\r
6019    the top, with a row of buttons below */\r
6020 VOID\r
6021 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6022 {\r
6023   RECT rectText;\r
6024   int newTextHeight, newTextWidth;\r
6025   ResizeEditPlusButtonsClosure cl;\r
6026   \r
6027   /*if (IsIconic(hDlg)) return;*/\r
6028   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6029   \r
6030   cl.hdwp = BeginDeferWindowPos(8);\r
6031 \r
6032   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6033   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6034   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6035   if (newTextHeight < 0) {\r
6036     newSizeY += -newTextHeight;\r
6037     newTextHeight = 0;\r
6038   }\r
6039   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6040     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6041 \r
6042   cl.hDlg = hDlg;\r
6043   cl.hText = hText;\r
6044   cl.sizeX = sizeX;\r
6045   cl.sizeY = sizeY;\r
6046   cl.newSizeX = newSizeX;\r
6047   cl.newSizeY = newSizeY;\r
6048   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6049 \r
6050   EndDeferWindowPos(cl.hdwp);\r
6051 }\r
6052 \r
6053 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6054 {\r
6055     RECT    rChild, rParent;\r
6056     int     wChild, hChild, wParent, hParent;\r
6057     int     wScreen, hScreen, xNew, yNew;\r
6058     HDC     hdc;\r
6059 \r
6060     /* Get the Height and Width of the child window */\r
6061     GetWindowRect (hwndChild, &rChild);\r
6062     wChild = rChild.right - rChild.left;\r
6063     hChild = rChild.bottom - rChild.top;\r
6064 \r
6065     /* Get the Height and Width of the parent window */\r
6066     GetWindowRect (hwndParent, &rParent);\r
6067     wParent = rParent.right - rParent.left;\r
6068     hParent = rParent.bottom - rParent.top;\r
6069 \r
6070     /* Get the display limits */\r
6071     hdc = GetDC (hwndChild);\r
6072     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6073     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6074     ReleaseDC(hwndChild, hdc);\r
6075 \r
6076     /* Calculate new X position, then adjust for screen */\r
6077     xNew = rParent.left + ((wParent - wChild) /2);\r
6078     if (xNew < 0) {\r
6079         xNew = 0;\r
6080     } else if ((xNew+wChild) > wScreen) {\r
6081         xNew = wScreen - wChild;\r
6082     }\r
6083 \r
6084     /* Calculate new Y position, then adjust for screen */\r
6085     if( mode == 0 ) {\r
6086         yNew = rParent.top  + ((hParent - hChild) /2);\r
6087     }\r
6088     else {\r
6089         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6090     }\r
6091 \r
6092     if (yNew < 0) {\r
6093         yNew = 0;\r
6094     } else if ((yNew+hChild) > hScreen) {\r
6095         yNew = hScreen - hChild;\r
6096     }\r
6097 \r
6098     /* Set it, and return */\r
6099     return SetWindowPos (hwndChild, NULL,\r
6100                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6101 }\r
6102 \r
6103 /* Center one window over another */\r
6104 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6105 {\r
6106     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6107 }\r
6108 \r
6109 /*---------------------------------------------------------------------------*\\r
6110  *\r
6111  * Startup Dialog functions\r
6112  *\r
6113 \*---------------------------------------------------------------------------*/\r
6114 void\r
6115 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6116 {\r
6117   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6118 \r
6119   while (*cd != NULL) {\r
6120     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6121     cd++;\r
6122   }\r
6123 }\r
6124 \r
6125 void\r
6126 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6127 {\r
6128   char buf1[MAX_ARG_LEN];\r
6129   int len;\r
6130 \r
6131   if (str[0] == '@') {\r
6132     FILE* f = fopen(str + 1, "r");\r
6133     if (f == NULL) {\r
6134       DisplayFatalError(str + 1, errno, 2);\r
6135       return;\r
6136     }\r
6137     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6138     fclose(f);\r
6139     buf1[len] = NULLCHAR;\r
6140     str = buf1;\r
6141   }\r
6142 \r
6143   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6144 \r
6145   for (;;) {\r
6146     char buf[MSG_SIZ];\r
6147     char *end = strchr(str, '\n');\r
6148     if (end == NULL) return;\r
6149     memcpy(buf, str, end - str);\r
6150     buf[end - str] = NULLCHAR;\r
6151     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6152     str = end + 1;\r
6153   }\r
6154 }\r
6155 \r
6156 void\r
6157 SetStartupDialogEnables(HWND hDlg)\r
6158 {\r
6159   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6160     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6161     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6162   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6163     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6164   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6165     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6166   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6167     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6168   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6169     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6170     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6171     IsDlgButtonChecked(hDlg, OPT_View));\r
6172 }\r
6173 \r
6174 char *\r
6175 QuoteForFilename(char *filename)\r
6176 {\r
6177   int dquote, space;\r
6178   dquote = strchr(filename, '"') != NULL;\r
6179   space = strchr(filename, ' ') != NULL;\r
6180   if (dquote || space) {\r
6181     if (dquote) {\r
6182       return "'";\r
6183     } else {\r
6184       return "\"";\r
6185     }\r
6186   } else {\r
6187     return "";\r
6188   }\r
6189 }\r
6190 \r
6191 VOID\r
6192 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6193 {\r
6194   char buf[MSG_SIZ];\r
6195   char *q;\r
6196 \r
6197   InitComboStringsFromOption(hwndCombo, nthnames);\r
6198   q = QuoteForFilename(nthcp);\r
6199     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6200   if (*nthdir != NULLCHAR) {\r
6201     q = QuoteForFilename(nthdir);\r
6202       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6203   }\r
6204   if (*nthcp == NULLCHAR) {\r
6205     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6206   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6207     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6208     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6209   }\r
6210 }\r
6211 \r
6212 LRESULT CALLBACK\r
6213 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6214 {\r
6215   char buf[MSG_SIZ];\r
6216   HANDLE hwndCombo;\r
6217   char *p;\r
6218 \r
6219   switch (message) {\r
6220   case WM_INITDIALOG:\r
6221     /* Center the dialog */\r
6222     CenterWindow (hDlg, GetDesktopWindow());\r
6223     Translate(hDlg, DLG_Startup);\r
6224     /* Initialize the dialog items */\r
6225     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6226                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6227                   firstChessProgramNames);\r
6228     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6229                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6230                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6231     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6232     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6233       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6234     if (*appData.icsHelper != NULLCHAR) {\r
6235       char *q = QuoteForFilename(appData.icsHelper);\r
6236       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6237     }\r
6238     if (*appData.icsHost == NULLCHAR) {\r
6239       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6240       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6241     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6242       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6243       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6244     }\r
6245 \r
6246     if (appData.icsActive) {\r
6247       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6248     }\r
6249     else if (appData.noChessProgram) {\r
6250       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6251     }\r
6252     else {\r
6253       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6254     }\r
6255 \r
6256     SetStartupDialogEnables(hDlg);\r
6257     return TRUE;\r
6258 \r
6259   case WM_COMMAND:\r
6260     switch (LOWORD(wParam)) {\r
6261     case IDOK:\r
6262       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6263         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6264         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6265         p = buf;\r
6266         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6267         ParseArgs(StringGet, &p);\r
6268         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6269         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6270         p = buf;\r
6271         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6272         ParseArgs(StringGet, &p);\r
6273         SwapEngines(singleList); // ... and then make it 'second'\r
6274 \r
6275         appData.noChessProgram = FALSE;\r
6276         appData.icsActive = FALSE;\r
6277       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6278         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6279         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6280         p = buf;\r
6281         ParseArgs(StringGet, &p);\r
6282         if (appData.zippyPlay) {\r
6283           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6284           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6285           p = buf;\r
6286           ParseArgs(StringGet, &p);\r
6287         }\r
6288       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6289         appData.noChessProgram = TRUE;\r
6290         appData.icsActive = FALSE;\r
6291       } else {\r
6292         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6293                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6294         return TRUE;\r
6295       }\r
6296       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6297         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6298         p = buf;\r
6299         ParseArgs(StringGet, &p);\r
6300       }\r
6301       EndDialog(hDlg, TRUE);\r
6302       return TRUE;\r
6303 \r
6304     case IDCANCEL:\r
6305       ExitEvent(0);\r
6306       return TRUE;\r
6307 \r
6308     case IDM_HELPCONTENTS:\r
6309       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6310         MessageBox (GetFocus(),\r
6311                     _("Unable to activate help"),\r
6312                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6313       }\r
6314       break;\r
6315 \r
6316     default:\r
6317       SetStartupDialogEnables(hDlg);\r
6318       break;\r
6319     }\r
6320     break;\r
6321   }\r
6322   return FALSE;\r
6323 }\r
6324 \r
6325 /*---------------------------------------------------------------------------*\\r
6326  *\r
6327  * About box dialog functions\r
6328  *\r
6329 \*---------------------------------------------------------------------------*/\r
6330 \r
6331 /* Process messages for "About" dialog box */\r
6332 LRESULT CALLBACK\r
6333 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6334 {\r
6335   switch (message) {\r
6336   case WM_INITDIALOG: /* message: initialize dialog box */\r
6337     /* Center the dialog over the application window */\r
6338     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6339     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6340     Translate(hDlg, ABOUTBOX);\r
6341     JAWS_COPYRIGHT\r
6342     return (TRUE);\r
6343 \r
6344   case WM_COMMAND: /* message: received a command */\r
6345     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6346         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6347       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6348       return (TRUE);\r
6349     }\r
6350     break;\r
6351   }\r
6352   return (FALSE);\r
6353 }\r
6354 \r
6355 /*---------------------------------------------------------------------------*\\r
6356  *\r
6357  * Comment Dialog functions\r
6358  *\r
6359 \*---------------------------------------------------------------------------*/\r
6360 \r
6361 LRESULT CALLBACK\r
6362 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6363 {\r
6364   static HANDLE hwndText = NULL;\r
6365   int len, newSizeX, newSizeY, flags;\r
6366   static int sizeX, sizeY;\r
6367   char *str;\r
6368   RECT rect;\r
6369   MINMAXINFO *mmi;\r
6370 \r
6371   switch (message) {\r
6372   case WM_INITDIALOG: /* message: initialize dialog box */\r
6373     /* Initialize the dialog items */\r
6374     Translate(hDlg, DLG_EditComment);\r
6375     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6376     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6377     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6378     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6379     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6380     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6381     SetWindowText(hDlg, commentTitle);\r
6382     if (editComment) {\r
6383       SetFocus(hwndText);\r
6384     } else {\r
6385       SetFocus(GetDlgItem(hDlg, IDOK));\r
6386     }\r
6387     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6388                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6389                 MAKELPARAM(FALSE, 0));\r
6390     /* Size and position the dialog */\r
6391     if (!commentDialog) {\r
6392       commentDialog = hDlg;\r
6393       flags = SWP_NOZORDER;\r
6394       GetClientRect(hDlg, &rect);\r
6395       sizeX = rect.right;\r
6396       sizeY = rect.bottom;\r
6397       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6398           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6399         WINDOWPLACEMENT wp;\r
6400         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6401         wp.length = sizeof(WINDOWPLACEMENT);\r
6402         wp.flags = 0;\r
6403         wp.showCmd = SW_SHOW;\r
6404         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6405         wp.rcNormalPosition.left = wpComment.x;\r
6406         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6407         wp.rcNormalPosition.top = wpComment.y;\r
6408         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6409         SetWindowPlacement(hDlg, &wp);\r
6410 \r
6411         GetClientRect(hDlg, &rect);\r
6412         newSizeX = rect.right;\r
6413         newSizeY = rect.bottom;\r
6414         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6415                               newSizeX, newSizeY);\r
6416         sizeX = newSizeX;\r
6417         sizeY = newSizeY;\r
6418       }\r
6419     }\r
6420     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6421     return FALSE;\r
6422 \r
6423   case WM_COMMAND: /* message: received a command */\r
6424     switch (LOWORD(wParam)) {\r
6425     case IDOK:\r
6426       if (editComment) {\r
6427         char *p, *q;\r
6428         /* Read changed options from the dialog box */\r
6429         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6430         len = GetWindowTextLength(hwndText);\r
6431         str = (char *) malloc(len + 1);\r
6432         GetWindowText(hwndText, str, len + 1);\r
6433         p = q = str;\r
6434         while (*q) {\r
6435           if (*q == '\r')\r
6436             q++;\r
6437           else\r
6438             *p++ = *q++;\r
6439         }\r
6440         *p = NULLCHAR;\r
6441         ReplaceComment(commentIndex, str);\r
6442         free(str);\r
6443       }\r
6444       CommentPopDown();\r
6445       return TRUE;\r
6446 \r
6447     case IDCANCEL:\r
6448     case OPT_CancelComment:\r
6449       CommentPopDown();\r
6450       return TRUE;\r
6451 \r
6452     case OPT_ClearComment:\r
6453       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6454       break;\r
6455 \r
6456     case OPT_EditComment:\r
6457       EditCommentEvent();\r
6458       return TRUE;\r
6459 \r
6460     default:\r
6461       break;\r
6462     }\r
6463     break;\r
6464 \r
6465   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6466         if( wParam == OPT_CommentText ) {\r
6467             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6468 \r
6469             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6470                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6471                 POINTL pt;\r
6472                 LRESULT index;\r
6473 \r
6474                 pt.x = LOWORD( lpMF->lParam );\r
6475                 pt.y = HIWORD( lpMF->lParam );\r
6476 \r
6477                 if(lpMF->msg == WM_CHAR) {\r
6478                         CHARRANGE sel;\r
6479                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6480                         index = sel.cpMin;\r
6481                 } else\r
6482                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6483 \r
6484                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6485                 len = GetWindowTextLength(hwndText);\r
6486                 str = (char *) malloc(len + 1);\r
6487                 GetWindowText(hwndText, str, len + 1);\r
6488                 ReplaceComment(commentIndex, str);\r
6489                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6490                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6491                 free(str);\r
6492 \r
6493                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6494                 lpMF->msg = WM_USER;\r
6495 \r
6496                 return TRUE;\r
6497             }\r
6498         }\r
6499         break;\r
6500 \r
6501   case WM_SIZE:\r
6502     newSizeX = LOWORD(lParam);\r
6503     newSizeY = HIWORD(lParam);\r
6504     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6505     sizeX = newSizeX;\r
6506     sizeY = newSizeY;\r
6507     break;\r
6508 \r
6509   case WM_GETMINMAXINFO:\r
6510     /* Prevent resizing window too small */\r
6511     mmi = (MINMAXINFO *) lParam;\r
6512     mmi->ptMinTrackSize.x = 100;\r
6513     mmi->ptMinTrackSize.y = 100;\r
6514     break;\r
6515   }\r
6516   return FALSE;\r
6517 }\r
6518 \r
6519 VOID\r
6520 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6521 {\r
6522   FARPROC lpProc;\r
6523   char *p, *q;\r
6524 \r
6525   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6526 \r
6527   if (str == NULL) str = "";\r
6528   p = (char *) malloc(2 * strlen(str) + 2);\r
6529   q = p;\r
6530   while (*str) {\r
6531     if (*str == '\n') *q++ = '\r';\r
6532     *q++ = *str++;\r
6533   }\r
6534   *q = NULLCHAR;\r
6535   if (commentText != NULL) free(commentText);\r
6536 \r
6537   commentIndex = index;\r
6538   commentTitle = title;\r
6539   commentText = p;\r
6540   editComment = edit;\r
6541 \r
6542   if (commentDialog) {\r
6543     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6544     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6545   } else {\r
6546     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6547     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6548                  hwndMain, (DLGPROC)lpProc);\r
6549     FreeProcInstance(lpProc);\r
6550   }\r
6551   commentUp = TRUE;\r
6552 }\r
6553 \r
6554 \r
6555 /*---------------------------------------------------------------------------*\\r
6556  *\r
6557  * Type-in move dialog functions\r
6558  * \r
6559 \*---------------------------------------------------------------------------*/\r
6560 \r
6561 LRESULT CALLBACK\r
6562 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6563 {\r
6564   char move[MSG_SIZ];\r
6565   HWND hInput;\r
6566 \r
6567   switch (message) {\r
6568   case WM_INITDIALOG:\r
6569     move[0] = (char) lParam;\r
6570     move[1] = NULLCHAR;\r
6571     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6572     Translate(hDlg, DLG_TypeInMove);\r
6573     hInput = GetDlgItem(hDlg, OPT_Move);\r
6574     SetWindowText(hInput, move);\r
6575     SetFocus(hInput);\r
6576     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6577     return FALSE;\r
6578 \r
6579   case WM_COMMAND:\r
6580     switch (LOWORD(wParam)) {\r
6581     case IDOK:\r
6582 \r
6583       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6584       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6585       TypeInDoneEvent(move);\r
6586       EndDialog(hDlg, TRUE);\r
6587       return TRUE;\r
6588     case IDCANCEL:\r
6589       EndDialog(hDlg, FALSE);\r
6590       return TRUE;\r
6591     default:\r
6592       break;\r
6593     }\r
6594     break;\r
6595   }\r
6596   return FALSE;\r
6597 }\r
6598 \r
6599 VOID\r
6600 PopUpMoveDialog(char firstchar)\r
6601 {\r
6602     FARPROC lpProc;\r
6603 \r
6604       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6605       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6606         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6607       FreeProcInstance(lpProc);\r
6608 }\r
6609 \r
6610 /*---------------------------------------------------------------------------*\\r
6611  *\r
6612  * Type-in name dialog functions\r
6613  * \r
6614 \*---------------------------------------------------------------------------*/\r
6615 \r
6616 LRESULT CALLBACK\r
6617 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6618 {\r
6619   char move[MSG_SIZ];\r
6620   HWND hInput;\r
6621 \r
6622   switch (message) {\r
6623   case WM_INITDIALOG:\r
6624     move[0] = (char) lParam;\r
6625     move[1] = NULLCHAR;\r
6626     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6627     Translate(hDlg, DLG_TypeInName);\r
6628     hInput = GetDlgItem(hDlg, OPT_Name);\r
6629     SetWindowText(hInput, move);\r
6630     SetFocus(hInput);\r
6631     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6632     return FALSE;\r
6633 \r
6634   case WM_COMMAND:\r
6635     switch (LOWORD(wParam)) {\r
6636     case IDOK:\r
6637       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6638       appData.userName = strdup(move);\r
6639       SetUserLogo();\r
6640       SetGameInfo();\r
6641       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6642         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6643         DisplayTitle(move);\r
6644       }\r
6645 \r
6646 \r
6647       EndDialog(hDlg, TRUE);\r
6648       return TRUE;\r
6649     case IDCANCEL:\r
6650       EndDialog(hDlg, FALSE);\r
6651       return TRUE;\r
6652     default:\r
6653       break;\r
6654     }\r
6655     break;\r
6656   }\r
6657   return FALSE;\r
6658 }\r
6659 \r
6660 VOID\r
6661 PopUpNameDialog(char firstchar)\r
6662 {\r
6663     FARPROC lpProc;\r
6664     \r
6665       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6666       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6667         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6668       FreeProcInstance(lpProc);\r
6669 }\r
6670 \r
6671 /*---------------------------------------------------------------------------*\\r
6672  *\r
6673  *  Error dialogs\r
6674  * \r
6675 \*---------------------------------------------------------------------------*/\r
6676 \r
6677 /* Nonmodal error box */\r
6678 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6679                              WPARAM wParam, LPARAM lParam);\r
6680 \r
6681 VOID\r
6682 ErrorPopUp(char *title, char *content)\r
6683 {\r
6684   FARPROC lpProc;\r
6685   char *p, *q;\r
6686   BOOLEAN modal = hwndMain == NULL;\r
6687 \r
6688   p = content;\r
6689   q = errorMessage;\r
6690   while (*p) {\r
6691     if (*p == '\n') {\r
6692       if (modal) {\r
6693         *q++ = ' ';\r
6694         p++;\r
6695       } else {\r
6696         *q++ = '\r';\r
6697         *q++ = *p++;\r
6698       }\r
6699     } else {\r
6700       *q++ = *p++;\r
6701     }\r
6702   }\r
6703   *q = NULLCHAR;\r
6704   strncpy(errorTitle, title, sizeof(errorTitle));\r
6705   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6706   \r
6707   if (modal) {\r
6708     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6709   } else {\r
6710     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6711     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6712                  hwndMain, (DLGPROC)lpProc);\r
6713     FreeProcInstance(lpProc);\r
6714   }\r
6715 }\r
6716 \r
6717 VOID\r
6718 ErrorPopDown()\r
6719 {\r
6720   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6721   if (errorDialog == NULL) return;\r
6722   DestroyWindow(errorDialog);\r
6723   errorDialog = NULL;\r
6724   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6725 }\r
6726 \r
6727 LRESULT CALLBACK\r
6728 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6729 {\r
6730   HANDLE hwndText;\r
6731   RECT rChild;\r
6732 \r
6733   switch (message) {\r
6734   case WM_INITDIALOG:\r
6735     GetWindowRect(hDlg, &rChild);\r
6736 \r
6737     /*\r
6738     SetWindowPos(hDlg, NULL, rChild.left,\r
6739       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6740       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6741     */\r
6742 \r
6743     /* \r
6744         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6745         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6746         and it doesn't work when you resize the dialog.\r
6747         For now, just give it a default position.\r
6748     */\r
6749     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6750     Translate(hDlg, DLG_Error);\r
6751 \r
6752     errorDialog = hDlg;\r
6753     SetWindowText(hDlg, errorTitle);\r
6754     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6755     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6756     return FALSE;\r
6757 \r
6758   case WM_COMMAND:\r
6759     switch (LOWORD(wParam)) {\r
6760     case IDOK:\r
6761     case IDCANCEL:\r
6762       if (errorDialog == hDlg) errorDialog = NULL;\r
6763       DestroyWindow(hDlg);\r
6764       return TRUE;\r
6765 \r
6766     default:\r
6767       break;\r
6768     }\r
6769     break;\r
6770   }\r
6771   return FALSE;\r
6772 }\r
6773 \r
6774 #ifdef GOTHIC\r
6775 HWND gothicDialog = NULL;\r
6776 \r
6777 LRESULT CALLBACK\r
6778 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6779 {\r
6780   HANDLE hwndText;\r
6781   RECT rChild;\r
6782   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6783 \r
6784   switch (message) {\r
6785   case WM_INITDIALOG:\r
6786     GetWindowRect(hDlg, &rChild);\r
6787 \r
6788     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6789                                                              SWP_NOZORDER);\r
6790 \r
6791     /* \r
6792         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6793         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6794         and it doesn't work when you resize the dialog.\r
6795         For now, just give it a default position.\r
6796     */\r
6797     gothicDialog = hDlg;\r
6798     SetWindowText(hDlg, errorTitle);\r
6799     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6800     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6801     return FALSE;\r
6802 \r
6803   case WM_COMMAND:\r
6804     switch (LOWORD(wParam)) {\r
6805     case IDOK:\r
6806     case IDCANCEL:\r
6807       if (errorDialog == hDlg) errorDialog = NULL;\r
6808       DestroyWindow(hDlg);\r
6809       return TRUE;\r
6810 \r
6811     default:\r
6812       break;\r
6813     }\r
6814     break;\r
6815   }\r
6816   return FALSE;\r
6817 }\r
6818 \r
6819 VOID\r
6820 GothicPopUp(char *title, VariantClass variant)\r
6821 {\r
6822   FARPROC lpProc;\r
6823   static char *lastTitle;\r
6824 \r
6825   strncpy(errorTitle, title, sizeof(errorTitle));\r
6826   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6827 \r
6828   if(lastTitle != title && gothicDialog != NULL) {\r
6829     DestroyWindow(gothicDialog);\r
6830     gothicDialog = NULL;\r
6831   }\r
6832   if(variant != VariantNormal && gothicDialog == NULL) {\r
6833     title = lastTitle;\r
6834     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6835     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6836                  hwndMain, (DLGPROC)lpProc);\r
6837     FreeProcInstance(lpProc);\r
6838   }\r
6839 }\r
6840 #endif\r
6841 \r
6842 /*---------------------------------------------------------------------------*\\r
6843  *\r
6844  *  Ics Interaction console functions\r
6845  *\r
6846 \*---------------------------------------------------------------------------*/\r
6847 \r
6848 #define HISTORY_SIZE 64\r
6849 static char *history[HISTORY_SIZE];\r
6850 int histIn = 0, histP = 0;\r
6851 \r
6852 \r
6853 VOID\r
6854 SaveInHistory(char *cmd)\r
6855 {\r
6856   if (history[histIn] != NULL) {\r
6857     free(history[histIn]);\r
6858     history[histIn] = NULL;\r
6859   }\r
6860   if (*cmd == NULLCHAR) return;\r
6861   history[histIn] = StrSave(cmd);\r
6862   histIn = (histIn + 1) % HISTORY_SIZE;\r
6863   if (history[histIn] != NULL) {\r
6864     free(history[histIn]);\r
6865 \r
6866     history[histIn] = NULL;\r
6867   }\r
6868   histP = histIn;\r
6869 }\r
6870 \r
6871 char *\r
6872 PrevInHistory(char *cmd)\r
6873 {\r
6874   int newhp;\r
6875   if (histP == histIn) {\r
6876     if (history[histIn] != NULL) free(history[histIn]);\r
6877     history[histIn] = StrSave(cmd);\r
6878   }\r
6879   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6880   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6881   histP = newhp;\r
6882   return history[histP];\r
6883 }\r
6884 \r
6885 char *\r
6886 NextInHistory()\r
6887 {\r
6888   if (histP == histIn) return NULL;\r
6889   histP = (histP + 1) % HISTORY_SIZE;\r
6890   return history[histP];   \r
6891 }\r
6892 \r
6893 HMENU\r
6894 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6895 {\r
6896   HMENU hmenu, h;\r
6897   int i = 0;\r
6898   hmenu = LoadMenu(hInst, "TextMenu");\r
6899   h = GetSubMenu(hmenu, 0);\r
6900   while (e->item) {\r
6901     if (strcmp(e->item, "-") == 0) {\r
6902       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6903     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6904       int flags = MF_STRING, j = 0;\r
6905       if (e->item[0] == '|') {\r
6906         flags |= MF_MENUBARBREAK;\r
6907         j++;\r
6908       }\r
6909       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6910       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6911     }\r
6912     e++;\r
6913     i++;\r
6914   } \r
6915   return hmenu;\r
6916 }\r
6917 \r
6918 WNDPROC consoleTextWindowProc;\r
6919 \r
6920 void\r
6921 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6922 {\r
6923   char buf[MSG_SIZ], name[MSG_SIZ];\r
6924   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6925   CHARRANGE sel;\r
6926 \r
6927   if (!getname) {\r
6928     SetWindowText(hInput, command);\r
6929     if (immediate) {\r
6930       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6931     } else {\r
6932       sel.cpMin = 999999;\r
6933       sel.cpMax = 999999;\r
6934       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6935       SetFocus(hInput);\r
6936     }\r
6937     return;\r
6938   }    \r
6939   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6940   if (sel.cpMin == sel.cpMax) {\r
6941     /* Expand to surrounding word */\r
6942     TEXTRANGE tr;\r
6943     do {\r
6944       tr.chrg.cpMax = sel.cpMin;\r
6945       tr.chrg.cpMin = --sel.cpMin;\r
6946       if (sel.cpMin < 0) break;\r
6947       tr.lpstrText = name;\r
6948       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6949     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6950     sel.cpMin++;\r
6951 \r
6952     do {\r
6953       tr.chrg.cpMin = sel.cpMax;\r
6954       tr.chrg.cpMax = ++sel.cpMax;\r
6955       tr.lpstrText = name;\r
6956       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6957     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6958     sel.cpMax--;\r
6959 \r
6960     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6961       MessageBeep(MB_ICONEXCLAMATION);\r
6962       return;\r
6963     }\r
6964     tr.chrg = sel;\r
6965     tr.lpstrText = name;\r
6966     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6967   } else {\r
6968     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6969       MessageBeep(MB_ICONEXCLAMATION);\r
6970       return;\r
6971     }\r
6972     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6973   }\r
6974   if (immediate) {\r
6975     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6976     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6977     SetWindowText(hInput, buf);\r
6978     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6979   } else {\r
6980     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6981       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6982     SetWindowText(hInput, buf);\r
6983     sel.cpMin = 999999;\r
6984     sel.cpMax = 999999;\r
6985     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6986     SetFocus(hInput);\r
6987   }\r
6988 }\r
6989 \r
6990 LRESULT CALLBACK \r
6991 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6992 {\r
6993   HWND hInput;\r
6994   CHARRANGE sel;\r
6995 \r
6996   switch (message) {\r
6997   case WM_KEYDOWN:\r
6998     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6999     if(wParam=='R') return 0;\r
7000     switch (wParam) {\r
7001     case VK_PRIOR:\r
7002       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7003       return 0;\r
7004     case VK_NEXT:\r
7005       sel.cpMin = 999999;\r
7006       sel.cpMax = 999999;\r
7007       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7008       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7009       return 0;\r
7010     }\r
7011     break;\r
7012   case WM_CHAR:\r
7013    if(wParam != '\022') {\r
7014     if (wParam == '\t') {\r
7015       if (GetKeyState(VK_SHIFT) < 0) {\r
7016         /* shifted */\r
7017         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7018         if (buttonDesc[0].hwnd) {\r
7019           SetFocus(buttonDesc[0].hwnd);\r
7020         } else {\r
7021           SetFocus(hwndMain);\r
7022         }\r
7023       } else {\r
7024         /* unshifted */\r
7025         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7026       }\r
7027     } else {\r
7028       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7029       JAWS_DELETE( SetFocus(hInput); )\r
7030       SendMessage(hInput, message, wParam, lParam);\r
7031     }\r
7032     return 0;\r
7033    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7034    lParam = -1;\r
7035   case WM_RBUTTONDOWN:\r
7036     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7037       /* Move selection here if it was empty */\r
7038       POINT pt;\r
7039       pt.x = LOWORD(lParam);\r
7040       pt.y = HIWORD(lParam);\r
7041       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7042       if (sel.cpMin == sel.cpMax) {\r
7043         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7044         sel.cpMax = sel.cpMin;\r
7045         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7046       }\r
7047       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7048 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7049       POINT pt;\r
7050       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7051       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7052       if (sel.cpMin == sel.cpMax) {\r
7053         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7054         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7055       }\r
7056       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7057         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7058       }\r
7059       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7060       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7061       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7062       MenuPopup(hwnd, pt, hmenu, -1);\r
7063 }\r
7064     }\r
7065     return 0;\r
7066   case WM_RBUTTONUP:\r
7067     if (GetKeyState(VK_SHIFT) & ~1) {\r
7068       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7069         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7070     }\r
7071     return 0;\r
7072   case WM_PASTE:\r
7073     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7074     SetFocus(hInput);\r
7075     return SendMessage(hInput, message, wParam, lParam);\r
7076   case WM_MBUTTONDOWN:\r
7077     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7078   case WM_COMMAND:\r
7079     switch (LOWORD(wParam)) {\r
7080     case IDM_QuickPaste:\r
7081       {\r
7082         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7083         if (sel.cpMin == sel.cpMax) {\r
7084           MessageBeep(MB_ICONEXCLAMATION);\r
7085           return 0;\r
7086         }\r
7087         SendMessage(hwnd, WM_COPY, 0, 0);\r
7088         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7089         SendMessage(hInput, WM_PASTE, 0, 0);\r
7090         SetFocus(hInput);\r
7091         return 0;\r
7092       }\r
7093     case IDM_Cut:\r
7094       SendMessage(hwnd, WM_CUT, 0, 0);\r
7095       return 0;\r
7096     case IDM_Paste:\r
7097       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7098       return 0;\r
7099     case IDM_Copy:\r
7100       SendMessage(hwnd, WM_COPY, 0, 0);\r
7101       return 0;\r
7102     default:\r
7103       {\r
7104         int i = LOWORD(wParam) - IDM_CommandX;\r
7105         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7106             icsTextMenuEntry[i].command != NULL) {\r
7107           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7108                    icsTextMenuEntry[i].getname,\r
7109                    icsTextMenuEntry[i].immediate);\r
7110           return 0;\r
7111         }\r
7112       }\r
7113       break;\r
7114     }\r
7115     break;\r
7116   }\r
7117   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7118 }\r
7119 \r
7120 WNDPROC consoleInputWindowProc;\r
7121 \r
7122 LRESULT CALLBACK\r
7123 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7124 {\r
7125   char buf[MSG_SIZ];\r
7126   char *p;\r
7127   static BOOL sendNextChar = FALSE;\r
7128   static BOOL quoteNextChar = FALSE;\r
7129   InputSource *is = consoleInputSource;\r
7130   CHARFORMAT cf;\r
7131   CHARRANGE sel;\r
7132 \r
7133   switch (message) {\r
7134   case WM_CHAR:\r
7135     if (!appData.localLineEditing || sendNextChar) {\r
7136       is->buf[0] = (CHAR) wParam;\r
7137       is->count = 1;\r
7138       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7139       sendNextChar = FALSE;\r
7140       return 0;\r
7141     }\r
7142     if (quoteNextChar) {\r
7143       buf[0] = (char) wParam;\r
7144       buf[1] = NULLCHAR;\r
7145       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7146       quoteNextChar = FALSE;\r
7147       return 0;\r
7148     }\r
7149     switch (wParam) {\r
7150     case '\r':   /* Enter key */\r
7151       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7152       if (consoleEcho) SaveInHistory(is->buf);\r
7153       is->buf[is->count++] = '\n';\r
7154       is->buf[is->count] = NULLCHAR;\r
7155       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7156       if (consoleEcho) {\r
7157         ConsoleOutput(is->buf, is->count, TRUE);\r
7158       } else if (appData.localLineEditing) {\r
7159         ConsoleOutput("\n", 1, TRUE);\r
7160       }\r
7161       /* fall thru */\r
7162     case '\033': /* Escape key */\r
7163       SetWindowText(hwnd, "");\r
7164       cf.cbSize = sizeof(CHARFORMAT);\r
7165       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7166       if (consoleEcho) {\r
7167         cf.crTextColor = textAttribs[ColorNormal].color;\r
7168       } else {\r
7169         cf.crTextColor = COLOR_ECHOOFF;\r
7170       }\r
7171       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7172       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7173       return 0;\r
7174     case '\t':   /* Tab key */\r
7175       if (GetKeyState(VK_SHIFT) < 0) {\r
7176         /* shifted */\r
7177         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7178       } else {\r
7179         /* unshifted */\r
7180         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7181         if (buttonDesc[0].hwnd) {\r
7182           SetFocus(buttonDesc[0].hwnd);\r
7183         } else {\r
7184           SetFocus(hwndMain);\r
7185         }\r
7186       }\r
7187       return 0;\r
7188     case '\023': /* Ctrl+S */\r
7189       sendNextChar = TRUE;\r
7190       return 0;\r
7191     case '\021': /* Ctrl+Q */\r
7192       quoteNextChar = TRUE;\r
7193       return 0;\r
7194     JAWS_REPLAY\r
7195     default:\r
7196       break;\r
7197     }\r
7198     break;\r
7199   case WM_KEYDOWN:\r
7200     switch (wParam) {\r
7201     case VK_UP:\r
7202       GetWindowText(hwnd, buf, MSG_SIZ);\r
7203       p = PrevInHistory(buf);\r
7204       if (p != NULL) {\r
7205         SetWindowText(hwnd, p);\r
7206         sel.cpMin = 999999;\r
7207         sel.cpMax = 999999;\r
7208         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7209         return 0;\r
7210       }\r
7211       break;\r
7212     case VK_DOWN:\r
7213       p = NextInHistory();\r
7214       if (p != NULL) {\r
7215         SetWindowText(hwnd, p);\r
7216         sel.cpMin = 999999;\r
7217         sel.cpMax = 999999;\r
7218         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7219         return 0;\r
7220       }\r
7221       break;\r
7222     case VK_HOME:\r
7223     case VK_END:\r
7224       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7225       /* fall thru */\r
7226     case VK_PRIOR:\r
7227     case VK_NEXT:\r
7228       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7229       return 0;\r
7230     }\r
7231     break;\r
7232   case WM_MBUTTONDOWN:\r
7233     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7234       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7235     break;\r
7236   case WM_RBUTTONUP:\r
7237     if (GetKeyState(VK_SHIFT) & ~1) {\r
7238       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7239         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7240     } else {\r
7241       POINT pt;\r
7242       HMENU hmenu;\r
7243       hmenu = LoadMenu(hInst, "InputMenu");\r
7244       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7245       if (sel.cpMin == sel.cpMax) {\r
7246         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7247         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7248       }\r
7249       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7250         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7251       }\r
7252       pt.x = LOWORD(lParam);\r
7253       pt.y = HIWORD(lParam);\r
7254       MenuPopup(hwnd, pt, hmenu, -1);\r
7255     }\r
7256     return 0;\r
7257   case WM_COMMAND:\r
7258     switch (LOWORD(wParam)) { \r
7259     case IDM_Undo:\r
7260       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7261       return 0;\r
7262     case IDM_SelectAll:\r
7263       sel.cpMin = 0;\r
7264       sel.cpMax = -1; /*999999?*/\r
7265       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7266       return 0;\r
7267     case IDM_Cut:\r
7268       SendMessage(hwnd, WM_CUT, 0, 0);\r
7269       return 0;\r
7270     case IDM_Paste:\r
7271       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7272       return 0;\r
7273     case IDM_Copy:\r
7274       SendMessage(hwnd, WM_COPY, 0, 0);\r
7275       return 0;\r
7276     }\r
7277     break;\r
7278   }\r
7279   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7280 }\r
7281 \r
7282 #define CO_MAX  100000\r
7283 #define CO_TRIM   1000\r
7284 \r
7285 LRESULT CALLBACK\r
7286 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7287 {\r
7288   static SnapData sd;\r
7289   HWND hText, hInput;\r
7290   RECT rect;\r
7291   static int sizeX, sizeY;\r
7292   int newSizeX, newSizeY;\r
7293   MINMAXINFO *mmi;\r
7294   WORD wMask;\r
7295 \r
7296   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7297   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7298 \r
7299   switch (message) {\r
7300   case WM_NOTIFY:\r
7301     if (((NMHDR*)lParam)->code == EN_LINK)\r
7302     {\r
7303       ENLINK *pLink = (ENLINK*)lParam;\r
7304       if (pLink->msg == WM_LBUTTONUP)\r
7305       {\r
7306         TEXTRANGE tr;\r
7307 \r
7308         tr.chrg = pLink->chrg;\r
7309         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7310         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7311         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7312         free(tr.lpstrText);\r
7313       }\r
7314     }\r
7315     break;\r
7316   case WM_INITDIALOG: /* message: initialize dialog box */\r
7317     hwndConsole = hDlg;\r
7318     SetFocus(hInput);\r
7319     consoleTextWindowProc = (WNDPROC)\r
7320       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7321     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7322     consoleInputWindowProc = (WNDPROC)\r
7323       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7324     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7325     Colorize(ColorNormal, TRUE);\r
7326     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7327     ChangedConsoleFont();\r
7328     GetClientRect(hDlg, &rect);\r
7329     sizeX = rect.right;\r
7330     sizeY = rect.bottom;\r
7331     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7332         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7333       WINDOWPLACEMENT wp;\r
7334       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7335       wp.length = sizeof(WINDOWPLACEMENT);\r
7336       wp.flags = 0;\r
7337       wp.showCmd = SW_SHOW;\r
7338       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7339       wp.rcNormalPosition.left = wpConsole.x;\r
7340       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7341       wp.rcNormalPosition.top = wpConsole.y;\r
7342       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7343       SetWindowPlacement(hDlg, &wp);\r
7344     }\r
7345 \r
7346    // [HGM] Chessknight's change 2004-07-13\r
7347    else { /* Determine Defaults */\r
7348        WINDOWPLACEMENT wp;\r
7349        wpConsole.x = wpMain.width + 1;\r
7350        wpConsole.y = wpMain.y;\r
7351        wpConsole.width = screenWidth -  wpMain.width;\r
7352        wpConsole.height = wpMain.height;\r
7353        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7354        wp.length = sizeof(WINDOWPLACEMENT);\r
7355        wp.flags = 0;\r
7356        wp.showCmd = SW_SHOW;\r
7357        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7358        wp.rcNormalPosition.left = wpConsole.x;\r
7359        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7360        wp.rcNormalPosition.top = wpConsole.y;\r
7361        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7362        SetWindowPlacement(hDlg, &wp);\r
7363     }\r
7364 \r
7365    // Allow hText to highlight URLs and send notifications on them\r
7366    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7367    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7368    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7369    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7370 \r
7371     return FALSE;\r
7372 \r
7373   case WM_SETFOCUS:\r
7374     SetFocus(hInput);\r
7375     return 0;\r
7376 \r
7377   case WM_CLOSE:\r
7378     ExitEvent(0);\r
7379     /* not reached */\r
7380     break;\r
7381 \r
7382   case WM_SIZE:\r
7383     if (IsIconic(hDlg)) break;\r
7384     newSizeX = LOWORD(lParam);\r
7385     newSizeY = HIWORD(lParam);\r
7386     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7387       RECT rectText, rectInput;\r
7388       POINT pt;\r
7389       int newTextHeight, newTextWidth;\r
7390       GetWindowRect(hText, &rectText);\r
7391       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7392       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7393       if (newTextHeight < 0) {\r
7394         newSizeY += -newTextHeight;\r
7395         newTextHeight = 0;\r
7396       }\r
7397       SetWindowPos(hText, NULL, 0, 0,\r
7398         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7399       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7400       pt.x = rectInput.left;\r
7401       pt.y = rectInput.top + newSizeY - sizeY;\r
7402       ScreenToClient(hDlg, &pt);\r
7403       SetWindowPos(hInput, NULL, \r
7404         pt.x, pt.y, /* needs client coords */   \r
7405         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7406         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7407     }\r
7408     sizeX = newSizeX;\r
7409     sizeY = newSizeY;\r
7410     break;\r
7411 \r
7412   case WM_GETMINMAXINFO:\r
7413     /* Prevent resizing window too small */\r
7414     mmi = (MINMAXINFO *) lParam;\r
7415     mmi->ptMinTrackSize.x = 100;\r
7416     mmi->ptMinTrackSize.y = 100;\r
7417     break;\r
7418 \r
7419   /* [AS] Snapping */\r
7420   case WM_ENTERSIZEMOVE:\r
7421     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7422 \r
7423   case WM_SIZING:\r
7424     return OnSizing( &sd, hDlg, wParam, lParam );\r
7425 \r
7426   case WM_MOVING:\r
7427     return OnMoving( &sd, hDlg, wParam, lParam );\r
7428 \r
7429   case WM_EXITSIZEMOVE:\r
7430         UpdateICSWidth(hText);\r
7431     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7432   }\r
7433 \r
7434   return DefWindowProc(hDlg, message, wParam, lParam);\r
7435 }\r
7436 \r
7437 \r
7438 VOID\r
7439 ConsoleCreate()\r
7440 {\r
7441   HWND hCons;\r
7442   if (hwndConsole) return;\r
7443   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7444   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7445 }\r
7446 \r
7447 \r
7448 VOID\r
7449 ConsoleOutput(char* data, int length, int forceVisible)\r
7450 {\r
7451   HWND hText;\r
7452   int trim, exlen;\r
7453   char *p, *q;\r
7454   char buf[CO_MAX+1];\r
7455   POINT pEnd;\r
7456   RECT rect;\r
7457   static int delayLF = 0;\r
7458   CHARRANGE savesel, sel;\r
7459 \r
7460   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7461   p = data;\r
7462   q = buf;\r
7463   if (delayLF) {\r
7464     *q++ = '\r';\r
7465     *q++ = '\n';\r
7466     delayLF = 0;\r
7467   }\r
7468   while (length--) {\r
7469     if (*p == '\n') {\r
7470       if (*++p) {\r
7471         *q++ = '\r';\r
7472         *q++ = '\n';\r
7473       } else {\r
7474         delayLF = 1;\r
7475       }\r
7476     } else if (*p == '\007') {\r
7477        MyPlaySound(&sounds[(int)SoundBell]);\r
7478        p++;\r
7479     } else {\r
7480       *q++ = *p++;\r
7481     }\r
7482   }\r
7483   *q = NULLCHAR;\r
7484   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7485   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7486   /* Save current selection */\r
7487   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7488   exlen = GetWindowTextLength(hText);\r
7489   /* Find out whether current end of text is visible */\r
7490   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7491   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7492   /* Trim existing text if it's too long */\r
7493   if (exlen + (q - buf) > CO_MAX) {\r
7494     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7495     sel.cpMin = 0;\r
7496     sel.cpMax = trim;\r
7497     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7498     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7499     exlen -= trim;\r
7500     savesel.cpMin -= trim;\r
7501     savesel.cpMax -= trim;\r
7502     if (exlen < 0) exlen = 0;\r
7503     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7504     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7505   }\r
7506   /* Append the new text */\r
7507   sel.cpMin = exlen;\r
7508   sel.cpMax = exlen;\r
7509   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7510   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7511   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7512   if (forceVisible || exlen == 0 ||\r
7513       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7514        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7515     /* Scroll to make new end of text visible if old end of text\r
7516        was visible or new text is an echo of user typein */\r
7517     sel.cpMin = 9999999;\r
7518     sel.cpMax = 9999999;\r
7519     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7520     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7521     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7522     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7523   }\r
7524   if (savesel.cpMax == exlen || forceVisible) {\r
7525     /* Move insert point to new end of text if it was at the old\r
7526        end of text or if the new text is an echo of user typein */\r
7527     sel.cpMin = 9999999;\r
7528     sel.cpMax = 9999999;\r
7529     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7530   } else {\r
7531     /* Restore previous selection */\r
7532     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7533   }\r
7534   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7535 }\r
7536 \r
7537 /*---------*/\r
7538 \r
7539 \r
7540 void\r
7541 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7542 {\r
7543   char buf[100];\r
7544   char *str;\r
7545   COLORREF oldFg, oldBg;\r
7546   HFONT oldFont;\r
7547   RECT rect;\r
7548 \r
7549   if(copyNumber > 1)\r
7550     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7551 \r
7552   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7553   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7554   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7555 \r
7556   rect.left = x;\r
7557   rect.right = x + squareSize;\r
7558   rect.top  = y;\r
7559   rect.bottom = y + squareSize;\r
7560   str = buf;\r
7561 \r
7562   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7563                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7564              y, ETO_CLIPPED|ETO_OPAQUE,\r
7565              &rect, str, strlen(str), NULL);\r
7566 \r
7567   (void) SetTextColor(hdc, oldFg);\r
7568   (void) SetBkColor(hdc, oldBg);\r
7569   (void) SelectObject(hdc, oldFont);\r
7570 }\r
7571 \r
7572 void\r
7573 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7574               RECT *rect, char *color, char *flagFell)\r
7575 {\r
7576   char buf[100];\r
7577   char *str;\r
7578   COLORREF oldFg, oldBg;\r
7579   HFONT oldFont;\r
7580 \r
7581   if (twoBoards && partnerUp) return;\r
7582   if (appData.clockMode) {\r
7583     if (tinyLayout)\r
7584       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7585     else\r
7586       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7587     str = buf;\r
7588   } else {\r
7589     str = color;\r
7590   }\r
7591 \r
7592   if (highlight) {\r
7593     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7594     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7595   } else {\r
7596     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7597     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7598   }\r
7599   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7600 \r
7601   JAWS_SILENCE\r
7602 \r
7603   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7604              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7605              rect, str, strlen(str), NULL);\r
7606   if(logoHeight > 0 && appData.clockMode) {\r
7607       RECT r;\r
7608       str += strlen(color)+2;\r
7609       r.top = rect->top + logoHeight/2;\r
7610       r.left = rect->left;\r
7611       r.right = rect->right;\r
7612       r.bottom = rect->bottom;\r
7613       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7614                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7615                  &r, str, strlen(str), NULL);\r
7616   }\r
7617   (void) SetTextColor(hdc, oldFg);\r
7618   (void) SetBkColor(hdc, oldBg);\r
7619   (void) SelectObject(hdc, oldFont);\r
7620 }\r
7621 \r
7622 \r
7623 int\r
7624 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7625            OVERLAPPED *ovl)\r
7626 {\r
7627   int ok, err;\r
7628 \r
7629   /* [AS]  */\r
7630   if( count <= 0 ) {\r
7631     if (appData.debugMode) {\r
7632       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7633     }\r
7634 \r
7635     return ERROR_INVALID_USER_BUFFER;\r
7636   }\r
7637 \r
7638   ResetEvent(ovl->hEvent);\r
7639   ovl->Offset = ovl->OffsetHigh = 0;\r
7640   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7641   if (ok) {\r
7642     err = NO_ERROR;\r
7643   } else {\r
7644     err = GetLastError();\r
7645     if (err == ERROR_IO_PENDING) {\r
7646       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7647       if (ok)\r
7648         err = NO_ERROR;\r
7649       else\r
7650         err = GetLastError();\r
7651     }\r
7652   }\r
7653   return err;\r
7654 }\r
7655 \r
7656 int\r
7657 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7658             OVERLAPPED *ovl)\r
7659 {\r
7660   int ok, err;\r
7661 \r
7662   ResetEvent(ovl->hEvent);\r
7663   ovl->Offset = ovl->OffsetHigh = 0;\r
7664   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7665   if (ok) {\r
7666     err = NO_ERROR;\r
7667   } else {\r
7668     err = GetLastError();\r
7669     if (err == ERROR_IO_PENDING) {\r
7670       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7671       if (ok)\r
7672         err = NO_ERROR;\r
7673       else\r
7674         err = GetLastError();\r
7675     }\r
7676   }\r
7677   return err;\r
7678 }\r
7679 \r
7680 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7681 void CheckForInputBufferFull( InputSource * is )\r
7682 {\r
7683     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7684         /* Look for end of line */\r
7685         char * p = is->buf;\r
7686         \r
7687         while( p < is->next && *p != '\n' ) {\r
7688             p++;\r
7689         }\r
7690 \r
7691         if( p >= is->next ) {\r
7692             if (appData.debugMode) {\r
7693                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7694             }\r
7695 \r
7696             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7697             is->count = (DWORD) -1;\r
7698             is->next = is->buf;\r
7699         }\r
7700     }\r
7701 }\r
7702 \r
7703 DWORD\r
7704 InputThread(LPVOID arg)\r
7705 {\r
7706   InputSource *is;\r
7707   OVERLAPPED ovl;\r
7708 \r
7709   is = (InputSource *) arg;\r
7710   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7711   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7712   while (is->hThread != NULL) {\r
7713     is->error = DoReadFile(is->hFile, is->next,\r
7714                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7715                            &is->count, &ovl);\r
7716     if (is->error == NO_ERROR) {\r
7717       is->next += is->count;\r
7718     } else {\r
7719       if (is->error == ERROR_BROKEN_PIPE) {\r
7720         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7721         is->count = 0;\r
7722       } else {\r
7723         is->count = (DWORD) -1;\r
7724         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7725         break; \r
7726       }\r
7727     }\r
7728 \r
7729     CheckForInputBufferFull( is );\r
7730 \r
7731     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7732 \r
7733     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7734 \r
7735     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7736   }\r
7737 \r
7738   CloseHandle(ovl.hEvent);\r
7739   CloseHandle(is->hFile);\r
7740 \r
7741   if (appData.debugMode) {\r
7742     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7743   }\r
7744 \r
7745   return 0;\r
7746 }\r
7747 \r
7748 \r
7749 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7750 DWORD\r
7751 NonOvlInputThread(LPVOID arg)\r
7752 {\r
7753   InputSource *is;\r
7754   char *p, *q;\r
7755   int i;\r
7756   char prev;\r
7757 \r
7758   is = (InputSource *) arg;\r
7759   while (is->hThread != NULL) {\r
7760     is->error = ReadFile(is->hFile, is->next,\r
7761                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7762                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7763     if (is->error == NO_ERROR) {\r
7764       /* Change CRLF to LF */\r
7765       if (is->next > is->buf) {\r
7766         p = is->next - 1;\r
7767         i = is->count + 1;\r
7768       } else {\r
7769         p = is->next;\r
7770         i = is->count;\r
7771       }\r
7772       q = p;\r
7773       prev = NULLCHAR;\r
7774       while (i > 0) {\r
7775         if (prev == '\r' && *p == '\n') {\r
7776           *(q-1) = '\n';\r
7777           is->count--;\r
7778         } else { \r
7779           *q++ = *p;\r
7780         }\r
7781         prev = *p++;\r
7782         i--;\r
7783       }\r
7784       *q = NULLCHAR;\r
7785       is->next = q;\r
7786     } else {\r
7787       if (is->error == ERROR_BROKEN_PIPE) {\r
7788         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7789         is->count = 0; \r
7790       } else {\r
7791         is->count = (DWORD) -1;\r
7792       }\r
7793     }\r
7794 \r
7795     CheckForInputBufferFull( is );\r
7796 \r
7797     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7798 \r
7799     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7800 \r
7801     if (is->count < 0) break;  /* Quit on error */\r
7802   }\r
7803   CloseHandle(is->hFile);\r
7804   return 0;\r
7805 }\r
7806 \r
7807 DWORD\r
7808 SocketInputThread(LPVOID arg)\r
7809 {\r
7810   InputSource *is;\r
7811 \r
7812   is = (InputSource *) arg;\r
7813   while (is->hThread != NULL) {\r
7814     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7815     if ((int)is->count == SOCKET_ERROR) {\r
7816       is->count = (DWORD) -1;\r
7817       is->error = WSAGetLastError();\r
7818     } else {\r
7819       is->error = NO_ERROR;\r
7820       is->next += is->count;\r
7821       if (is->count == 0 && is->second == is) {\r
7822         /* End of file on stderr; quit with no message */\r
7823         break;\r
7824       }\r
7825     }\r
7826     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7827 \r
7828     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7829 \r
7830     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7831   }\r
7832   return 0;\r
7833 }\r
7834 \r
7835 VOID\r
7836 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7837 {\r
7838   InputSource *is;\r
7839 \r
7840   is = (InputSource *) lParam;\r
7841   if (is->lineByLine) {\r
7842     /* Feed in lines one by one */\r
7843     char *p = is->buf;\r
7844     char *q = p;\r
7845     while (q < is->next) {\r
7846       if (*q++ == '\n') {\r
7847         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7848         p = q;\r
7849       }\r
7850     }\r
7851     \r
7852     /* Move any partial line to the start of the buffer */\r
7853     q = is->buf;\r
7854     while (p < is->next) {\r
7855       *q++ = *p++;\r
7856     }\r
7857     is->next = q;\r
7858 \r
7859     if (is->error != NO_ERROR || is->count == 0) {\r
7860       /* Notify backend of the error.  Note: If there was a partial\r
7861          line at the end, it is not flushed through. */\r
7862       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7863     }\r
7864   } else {\r
7865     /* Feed in the whole chunk of input at once */\r
7866     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7867     is->next = is->buf;\r
7868   }\r
7869 }\r
7870 \r
7871 /*---------------------------------------------------------------------------*\\r
7872  *\r
7873  *  Menu enables. Used when setting various modes.\r
7874  *\r
7875 \*---------------------------------------------------------------------------*/\r
7876 \r
7877 typedef struct {\r
7878   int item;\r
7879   int flags;\r
7880 } Enables;\r
7881 \r
7882 VOID\r
7883 GreyRevert(Boolean grey)\r
7884 { // [HGM] vari: for retracting variations in local mode\r
7885   HMENU hmenu = GetMenu(hwndMain);\r
7886   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7887   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7888 }\r
7889 \r
7890 VOID\r
7891 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7892 {\r
7893   while (enab->item > 0) {\r
7894     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7895     enab++;\r
7896   }\r
7897 }\r
7898 \r
7899 Enables gnuEnables[] = {\r
7900   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7913 \r
7914   // Needed to switch from ncp to GNU mode on Engine Load\r
7915   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7916   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7928   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7929   { -1, -1 }\r
7930 };\r
7931 \r
7932 Enables icsEnables[] = {\r
7933   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7941   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7942   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7943   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7944   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7949   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7953   { -1, -1 }\r
7954 };\r
7955 \r
7956 #if ZIPPY\r
7957 Enables zippyEnables[] = {\r
7958   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7959   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7960   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7961   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7962   { -1, -1 }\r
7963 };\r
7964 #endif\r
7965 \r
7966 Enables ncpEnables[] = {\r
7967   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7976   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7977   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7981   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7986   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7987   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7988   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7989   { -1, -1 }\r
7990 };\r
7991 \r
7992 Enables trainingOnEnables[] = {\r
7993   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7994   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7995   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8001   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8002   { -1, -1 }\r
8003 };\r
8004 \r
8005 Enables trainingOffEnables[] = {\r
8006   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8007   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8008   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8009   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8010   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8011   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8012   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8013   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8014   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8015   { -1, -1 }\r
8016 };\r
8017 \r
8018 /* These modify either ncpEnables or gnuEnables */\r
8019 Enables cmailEnables[] = {\r
8020   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8021   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8022   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8023   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8025   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8027   { -1, -1 }\r
8028 };\r
8029 \r
8030 Enables machineThinkingEnables[] = {\r
8031   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8042   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8043   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8044 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8045   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8046   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8047   { -1, -1 }\r
8048 };\r
8049 \r
8050 Enables userThinkingEnables[] = {\r
8051   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8052   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8053   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8054   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8055   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8056   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8057   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8058   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8059   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8060   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8061   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8062   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8063   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8064 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8065   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8066   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8067   { -1, -1 }\r
8068 };\r
8069 \r
8070 /*---------------------------------------------------------------------------*\\r
8071  *\r
8072  *  Front-end interface functions exported by XBoard.\r
8073  *  Functions appear in same order as prototypes in frontend.h.\r
8074  * \r
8075 \*---------------------------------------------------------------------------*/\r
8076 VOID\r
8077 CheckMark(UINT item, int state)\r
8078 {\r
8079     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8080 }\r
8081 \r
8082 VOID\r
8083 ModeHighlight()\r
8084 {\r
8085   static UINT prevChecked = 0;\r
8086   static int prevPausing = 0;\r
8087   UINT nowChecked;\r
8088 \r
8089   if (pausing != prevPausing) {\r
8090     prevPausing = pausing;\r
8091     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8092                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8093     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8094   }\r
8095 \r
8096   switch (gameMode) {\r
8097   case BeginningOfGame:\r
8098     if (appData.icsActive)\r
8099       nowChecked = IDM_IcsClient;\r
8100     else if (appData.noChessProgram)\r
8101       nowChecked = IDM_EditGame;\r
8102     else\r
8103       nowChecked = IDM_MachineBlack;\r
8104     break;\r
8105   case MachinePlaysBlack:\r
8106     nowChecked = IDM_MachineBlack;\r
8107     break;\r
8108   case MachinePlaysWhite:\r
8109     nowChecked = IDM_MachineWhite;\r
8110     break;\r
8111   case TwoMachinesPlay:\r
8112     nowChecked = IDM_TwoMachines;\r
8113     break;\r
8114   case AnalyzeMode:\r
8115     nowChecked = IDM_AnalysisMode;\r
8116     break;\r
8117   case AnalyzeFile:\r
8118     nowChecked = IDM_AnalyzeFile;\r
8119     break;\r
8120   case EditGame:\r
8121     nowChecked = IDM_EditGame;\r
8122     break;\r
8123   case PlayFromGameFile:\r
8124     nowChecked = IDM_LoadGame;\r
8125     break;\r
8126   case EditPosition:\r
8127     nowChecked = IDM_EditPosition;\r
8128     break;\r
8129   case Training:\r
8130     nowChecked = IDM_Training;\r
8131     break;\r
8132   case IcsPlayingWhite:\r
8133   case IcsPlayingBlack:\r
8134   case IcsObserving:\r
8135   case IcsIdle:\r
8136     nowChecked = IDM_IcsClient;\r
8137     break;\r
8138   default:\r
8139   case EndOfGame:\r
8140     nowChecked = 0;\r
8141     break;\r
8142   }\r
8143   CheckMark(prevChecked, MF_UNCHECKED);\r
8144   CheckMark(nowChecked, MF_CHECKED);\r
8145   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8146 \r
8147   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8148     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8149                           MF_BYCOMMAND|MF_ENABLED);\r
8150   } else {\r
8151     (void) EnableMenuItem(GetMenu(hwndMain), \r
8152                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8153   }\r
8154 \r
8155   prevChecked = nowChecked;\r
8156 \r
8157   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8158   if (appData.icsActive) {\r
8159        if (appData.icsEngineAnalyze) {\r
8160                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8161        } else {\r
8162                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8163        }\r
8164   }\r
8165   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8166 }\r
8167 \r
8168 VOID\r
8169 SetICSMode()\r
8170 {\r
8171   HMENU hmenu = GetMenu(hwndMain);\r
8172   SetMenuEnables(hmenu, icsEnables);\r
8173   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8174     MF_BYCOMMAND|MF_ENABLED);\r
8175 #if ZIPPY\r
8176   if (appData.zippyPlay) {\r
8177     SetMenuEnables(hmenu, zippyEnables);\r
8178     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8179          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8180           MF_BYCOMMAND|MF_ENABLED);\r
8181   }\r
8182 #endif\r
8183 }\r
8184 \r
8185 VOID\r
8186 SetGNUMode()\r
8187 {\r
8188   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8189 }\r
8190 \r
8191 VOID\r
8192 SetNCPMode()\r
8193 {\r
8194   HMENU hmenu = GetMenu(hwndMain);\r
8195   SetMenuEnables(hmenu, ncpEnables);\r
8196     DrawMenuBar(hwndMain);\r
8197 }\r
8198 \r
8199 VOID\r
8200 SetCmailMode()\r
8201 {\r
8202   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8203 }\r
8204 \r
8205 VOID \r
8206 SetTrainingModeOn()\r
8207 {\r
8208   int i;\r
8209   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8210   for (i = 0; i < N_BUTTONS; i++) {\r
8211     if (buttonDesc[i].hwnd != NULL)\r
8212       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8213   }\r
8214   CommentPopDown();\r
8215 }\r
8216 \r
8217 VOID SetTrainingModeOff()\r
8218 {\r
8219   int i;\r
8220   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8221   for (i = 0; i < N_BUTTONS; i++) {\r
8222     if (buttonDesc[i].hwnd != NULL)\r
8223       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8224   }\r
8225 }\r
8226 \r
8227 \r
8228 VOID\r
8229 SetUserThinkingEnables()\r
8230 {\r
8231   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8232 }\r
8233 \r
8234 VOID\r
8235 SetMachineThinkingEnables()\r
8236 {\r
8237   HMENU hMenu = GetMenu(hwndMain);\r
8238   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8239 \r
8240   SetMenuEnables(hMenu, machineThinkingEnables);\r
8241 \r
8242   if (gameMode == MachinePlaysBlack) {\r
8243     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8244   } else if (gameMode == MachinePlaysWhite) {\r
8245     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8246   } else if (gameMode == TwoMachinesPlay) {\r
8247     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8248   }\r
8249 }\r
8250 \r
8251 \r
8252 VOID\r
8253 DisplayTitle(char *str)\r
8254 {\r
8255   char title[MSG_SIZ], *host;\r
8256   if (str[0] != NULLCHAR) {\r
8257     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8258   } else if (appData.icsActive) {\r
8259     if (appData.icsCommPort[0] != NULLCHAR)\r
8260       host = "ICS";\r
8261     else \r
8262       host = appData.icsHost;\r
8263       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8264   } else if (appData.noChessProgram) {\r
8265     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8266   } else {\r
8267     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8268     strcat(title, ": ");\r
8269     strcat(title, first.tidy);\r
8270   }\r
8271   SetWindowText(hwndMain, title);\r
8272 }\r
8273 \r
8274 \r
8275 VOID\r
8276 DisplayMessage(char *str1, char *str2)\r
8277 {\r
8278   HDC hdc;\r
8279   HFONT oldFont;\r
8280   int remain = MESSAGE_TEXT_MAX - 1;\r
8281   int len;\r
8282 \r
8283   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8284   messageText[0] = NULLCHAR;\r
8285   if (*str1) {\r
8286     len = strlen(str1);\r
8287     if (len > remain) len = remain;\r
8288     strncpy(messageText, str1, len);\r
8289     messageText[len] = NULLCHAR;\r
8290     remain -= len;\r
8291   }\r
8292   if (*str2 && remain >= 2) {\r
8293     if (*str1) {\r
8294       strcat(messageText, "  ");\r
8295       remain -= 2;\r
8296     }\r
8297     len = strlen(str2);\r
8298     if (len > remain) len = remain;\r
8299     strncat(messageText, str2, len);\r
8300   }\r
8301   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8302   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8303 \r
8304   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8305 \r
8306   SAYMACHINEMOVE();\r
8307 \r
8308   hdc = GetDC(hwndMain);\r
8309   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8310   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8311              &messageRect, messageText, strlen(messageText), NULL);\r
8312   (void) SelectObject(hdc, oldFont);\r
8313   (void) ReleaseDC(hwndMain, hdc);\r
8314 }\r
8315 \r
8316 VOID\r
8317 DisplayError(char *str, int error)\r
8318 {\r
8319   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8320   int len;\r
8321 \r
8322   if (error == 0) {\r
8323     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8324   } else {\r
8325     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8326                         NULL, error, LANG_NEUTRAL,\r
8327                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8328     if (len > 0) {\r
8329       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8330     } else {\r
8331       ErrorMap *em = errmap;\r
8332       while (em->err != 0 && em->err != error) em++;\r
8333       if (em->err != 0) {\r
8334         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8335       } else {\r
8336         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8337       }\r
8338     }\r
8339   }\r
8340   \r
8341   ErrorPopUp(_("Error"), buf);\r
8342 }\r
8343 \r
8344 \r
8345 VOID\r
8346 DisplayMoveError(char *str)\r
8347 {\r
8348   fromX = fromY = -1;\r
8349   ClearHighlights();\r
8350   DrawPosition(FALSE, NULL);\r
8351   if (appData.popupMoveErrors) {\r
8352     ErrorPopUp(_("Error"), str);\r
8353   } else {\r
8354     DisplayMessage(str, "");\r
8355     moveErrorMessageUp = TRUE;\r
8356   }\r
8357 }\r
8358 \r
8359 VOID\r
8360 DisplayFatalError(char *str, int error, int exitStatus)\r
8361 {\r
8362   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8363   int len;\r
8364   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8365 \r
8366   if (error != 0) {\r
8367     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8368                         NULL, error, LANG_NEUTRAL,\r
8369                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8370     if (len > 0) {\r
8371       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8372     } else {\r
8373       ErrorMap *em = errmap;\r
8374       while (em->err != 0 && em->err != error) em++;\r
8375       if (em->err != 0) {\r
8376         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8377       } else {\r
8378         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8379       }\r
8380     }\r
8381     str = buf;\r
8382   }\r
8383   if (appData.debugMode) {\r
8384     fprintf(debugFP, "%s: %s\n", label, str);\r
8385   }\r
8386   if (appData.popupExitMessage) {\r
8387     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8388                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8389   }\r
8390   ExitEvent(exitStatus);\r
8391 }\r
8392 \r
8393 \r
8394 VOID\r
8395 DisplayInformation(char *str)\r
8396 {\r
8397   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8398 }\r
8399 \r
8400 \r
8401 VOID\r
8402 DisplayNote(char *str)\r
8403 {\r
8404   ErrorPopUp(_("Note"), str);\r
8405 }\r
8406 \r
8407 \r
8408 typedef struct {\r
8409   char *title, *question, *replyPrefix;\r
8410   ProcRef pr;\r
8411 } QuestionParams;\r
8412 \r
8413 LRESULT CALLBACK\r
8414 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8415 {\r
8416   static QuestionParams *qp;\r
8417   char reply[MSG_SIZ];\r
8418   int len, err;\r
8419 \r
8420   switch (message) {\r
8421   case WM_INITDIALOG:\r
8422     qp = (QuestionParams *) lParam;\r
8423     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8424     Translate(hDlg, DLG_Question);\r
8425     SetWindowText(hDlg, qp->title);\r
8426     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8427     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8428     return FALSE;\r
8429 \r
8430   case WM_COMMAND:\r
8431     switch (LOWORD(wParam)) {\r
8432     case IDOK:\r
8433       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8434       if (*reply) strcat(reply, " ");\r
8435       len = strlen(reply);\r
8436       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8437       strcat(reply, "\n");\r
8438       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8439       EndDialog(hDlg, TRUE);\r
8440       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8441       return TRUE;\r
8442     case IDCANCEL:\r
8443       EndDialog(hDlg, FALSE);\r
8444       return TRUE;\r
8445     default:\r
8446       break;\r
8447     }\r
8448     break;\r
8449   }\r
8450   return FALSE;\r
8451 }\r
8452 \r
8453 VOID\r
8454 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8455 {\r
8456     QuestionParams qp;\r
8457     FARPROC lpProc;\r
8458     \r
8459     qp.title = title;\r
8460     qp.question = question;\r
8461     qp.replyPrefix = replyPrefix;\r
8462     qp.pr = pr;\r
8463     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8464     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8465       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8466     FreeProcInstance(lpProc);\r
8467 }\r
8468 \r
8469 /* [AS] Pick FRC position */\r
8470 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8471 {\r
8472     static int * lpIndexFRC;\r
8473     BOOL index_is_ok;\r
8474     char buf[16];\r
8475 \r
8476     switch( message )\r
8477     {\r
8478     case WM_INITDIALOG:\r
8479         lpIndexFRC = (int *) lParam;\r
8480 \r
8481         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8482         Translate(hDlg, DLG_NewGameFRC);\r
8483 \r
8484         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8485         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8486         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8487         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8488 \r
8489         break;\r
8490 \r
8491     case WM_COMMAND:\r
8492         switch( LOWORD(wParam) ) {\r
8493         case IDOK:\r
8494             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8495             EndDialog( hDlg, 0 );\r
8496             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8497             return TRUE;\r
8498         case IDCANCEL:\r
8499             EndDialog( hDlg, 1 );   \r
8500             return TRUE;\r
8501         case IDC_NFG_Edit:\r
8502             if( HIWORD(wParam) == EN_CHANGE ) {\r
8503                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8504 \r
8505                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8506             }\r
8507             return TRUE;\r
8508         case IDC_NFG_Random:\r
8509           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8510             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8511             return TRUE;\r
8512         }\r
8513 \r
8514         break;\r
8515     }\r
8516 \r
8517     return FALSE;\r
8518 }\r
8519 \r
8520 int NewGameFRC()\r
8521 {\r
8522     int result;\r
8523     int index = appData.defaultFrcPosition;\r
8524     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8525 \r
8526     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8527 \r
8528     if( result == 0 ) {\r
8529         appData.defaultFrcPosition = index;\r
8530     }\r
8531 \r
8532     return result;\r
8533 }\r
8534 \r
8535 /* [AS] Game list options. Refactored by HGM */\r
8536 \r
8537 HWND gameListOptionsDialog;\r
8538 \r
8539 // low-level front-end: clear text edit / list widget\r
8540 void\r
8541 \r
8542 GLT_ClearList()\r
8543 {\r
8544     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8545 }\r
8546 \r
8547 // low-level front-end: clear text edit / list widget\r
8548 void\r
8549 GLT_DeSelectList()\r
8550 {\r
8551     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8552 }\r
8553 \r
8554 // low-level front-end: append line to text edit / list widget\r
8555 void\r
8556 GLT_AddToList( char *name )\r
8557 {\r
8558     if( name != 0 ) {\r
8559             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8560     }\r
8561 }\r
8562 \r
8563 // low-level front-end: get line from text edit / list widget\r
8564 Boolean\r
8565 GLT_GetFromList( int index, char *name )\r
8566 {\r
8567     if( name != 0 ) {\r
8568             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8569                 return TRUE;\r
8570     }\r
8571     return FALSE;\r
8572 }\r
8573 \r
8574 void GLT_MoveSelection( HWND hDlg, int delta )\r
8575 {\r
8576     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8577     int idx2 = idx1 + delta;\r
8578     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8579 \r
8580     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8581         char buf[128];\r
8582 \r
8583         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8584         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8585         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8586         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8587     }\r
8588 }\r
8589 \r
8590 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8591 {\r
8592     switch( message )\r
8593     {\r
8594     case WM_INITDIALOG:\r
8595         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8596         \r
8597         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8598         Translate(hDlg, DLG_GameListOptions);\r
8599 \r
8600         /* Initialize list */\r
8601         GLT_TagsToList( lpUserGLT );\r
8602 \r
8603         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8604 \r
8605         break;\r
8606 \r
8607     case WM_COMMAND:\r
8608         switch( LOWORD(wParam) ) {\r
8609         case IDOK:\r
8610             GLT_ParseList();\r
8611             EndDialog( hDlg, 0 );\r
8612             return TRUE;\r
8613         case IDCANCEL:\r
8614             EndDialog( hDlg, 1 );\r
8615             return TRUE;\r
8616 \r
8617         case IDC_GLT_Default:\r
8618             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8619             return TRUE;\r
8620 \r
8621         case IDC_GLT_Restore:\r
8622             GLT_TagsToList( appData.gameListTags );\r
8623             return TRUE;\r
8624 \r
8625         case IDC_GLT_Up:\r
8626             GLT_MoveSelection( hDlg, -1 );\r
8627             return TRUE;\r
8628 \r
8629         case IDC_GLT_Down:\r
8630             GLT_MoveSelection( hDlg, +1 );\r
8631             return TRUE;\r
8632         }\r
8633 \r
8634         break;\r
8635     }\r
8636 \r
8637     return FALSE;\r
8638 }\r
8639 \r
8640 int GameListOptions()\r
8641 {\r
8642     int result;\r
8643     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8644 \r
8645       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8646 \r
8647     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8648 \r
8649     if( result == 0 ) {\r
8650         /* [AS] Memory leak here! */\r
8651         appData.gameListTags = strdup( lpUserGLT ); \r
8652     }\r
8653 \r
8654     return result;\r
8655 }\r
8656 \r
8657 VOID\r
8658 DisplayIcsInteractionTitle(char *str)\r
8659 {\r
8660   char consoleTitle[MSG_SIZ];\r
8661 \r
8662     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8663     SetWindowText(hwndConsole, consoleTitle);\r
8664 \r
8665     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8666       char buf[MSG_SIZ], *p = buf, *q;\r
8667         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8668       do {\r
8669         q = strchr(p, ';');\r
8670         if(q) *q++ = 0;\r
8671         if(*p) ChatPopUp(p);\r
8672       } while(p=q);\r
8673     }\r
8674 \r
8675     SetActiveWindow(hwndMain);\r
8676 }\r
8677 \r
8678 void\r
8679 DrawPosition(int fullRedraw, Board board)\r
8680 {\r
8681   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8682 }\r
8683 \r
8684 void NotifyFrontendLogin()\r
8685 {\r
8686         if (hwndConsole)\r
8687                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8688 }\r
8689 \r
8690 VOID\r
8691 ResetFrontEnd()\r
8692 {\r
8693   fromX = fromY = -1;\r
8694   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8695     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8696     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8697     dragInfo.lastpos = dragInfo.pos;\r
8698     dragInfo.start.x = dragInfo.start.y = -1;\r
8699     dragInfo.from = dragInfo.start;\r
8700     ReleaseCapture();\r
8701     DrawPosition(TRUE, NULL);\r
8702   }\r
8703   TagsPopDown();\r
8704 }\r
8705 \r
8706 \r
8707 VOID\r
8708 CommentPopUp(char *title, char *str)\r
8709 {\r
8710   HWND hwnd = GetActiveWindow();\r
8711   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8712   SAY(str);\r
8713   SetActiveWindow(hwnd);\r
8714 }\r
8715 \r
8716 VOID\r
8717 CommentPopDown(void)\r
8718 {\r
8719   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8720   if (commentDialog) {\r
8721     ShowWindow(commentDialog, SW_HIDE);\r
8722   }\r
8723   commentUp = FALSE;\r
8724 }\r
8725 \r
8726 VOID\r
8727 EditCommentPopUp(int index, char *title, char *str)\r
8728 {\r
8729   EitherCommentPopUp(index, title, str, TRUE);\r
8730 }\r
8731 \r
8732 \r
8733 int\r
8734 Roar()\r
8735 {\r
8736   MyPlaySound(&sounds[(int)SoundRoar]);\r
8737   return 1;\r
8738 }\r
8739 \r
8740 VOID\r
8741 RingBell()\r
8742 {\r
8743   MyPlaySound(&sounds[(int)SoundMove]);\r
8744 }\r
8745 \r
8746 VOID PlayIcsWinSound()\r
8747 {\r
8748   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8749 }\r
8750 \r
8751 VOID PlayIcsLossSound()\r
8752 {\r
8753   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8754 }\r
8755 \r
8756 VOID PlayIcsDrawSound()\r
8757 {\r
8758   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8759 }\r
8760 \r
8761 VOID PlayIcsUnfinishedSound()\r
8762 {\r
8763   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8764 }\r
8765 \r
8766 VOID\r
8767 PlayAlarmSound()\r
8768 {\r
8769   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8770 }\r
8771 \r
8772 VOID\r
8773 PlayTellSound()\r
8774 {\r
8775   MyPlaySound(&textAttribs[ColorTell].sound);\r
8776 }\r
8777 \r
8778 \r
8779 VOID\r
8780 EchoOn()\r
8781 {\r
8782   HWND hInput;\r
8783   consoleEcho = TRUE;\r
8784   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8785   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8786   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8787 }\r
8788 \r
8789 \r
8790 VOID\r
8791 EchoOff()\r
8792 {\r
8793   CHARFORMAT cf;\r
8794   HWND hInput;\r
8795   consoleEcho = FALSE;\r
8796   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8797   /* This works OK: set text and background both to the same color */\r
8798   cf = consoleCF;\r
8799   cf.crTextColor = COLOR_ECHOOFF;\r
8800   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8801   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8802 }\r
8803 \r
8804 /* No Raw()...? */\r
8805 \r
8806 void Colorize(ColorClass cc, int continuation)\r
8807 {\r
8808   currentColorClass = cc;\r
8809   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8810   consoleCF.crTextColor = textAttribs[cc].color;\r
8811   consoleCF.dwEffects = textAttribs[cc].effects;\r
8812   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8813 }\r
8814 \r
8815 char *\r
8816 UserName()\r
8817 {\r
8818   static char buf[MSG_SIZ];\r
8819   DWORD bufsiz = MSG_SIZ;\r
8820 \r
8821   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8822         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8823   }\r
8824   if (!GetUserName(buf, &bufsiz)) {\r
8825     /*DisplayError("Error getting user name", GetLastError());*/\r
8826     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8827   }\r
8828   return buf;\r
8829 }\r
8830 \r
8831 char *\r
8832 HostName()\r
8833 {\r
8834   static char buf[MSG_SIZ];\r
8835   DWORD bufsiz = MSG_SIZ;\r
8836 \r
8837   if (!GetComputerName(buf, &bufsiz)) {\r
8838     /*DisplayError("Error getting host name", GetLastError());*/\r
8839     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8840   }\r
8841   return buf;\r
8842 }\r
8843 \r
8844 \r
8845 int\r
8846 ClockTimerRunning()\r
8847 {\r
8848   return clockTimerEvent != 0;\r
8849 }\r
8850 \r
8851 int\r
8852 StopClockTimer()\r
8853 {\r
8854   if (clockTimerEvent == 0) return FALSE;\r
8855   KillTimer(hwndMain, clockTimerEvent);\r
8856   clockTimerEvent = 0;\r
8857   return TRUE;\r
8858 }\r
8859 \r
8860 void\r
8861 StartClockTimer(long millisec)\r
8862 {\r
8863   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8864                              (UINT) millisec, NULL);\r
8865 }\r
8866 \r
8867 void\r
8868 DisplayWhiteClock(long timeRemaining, int highlight)\r
8869 {\r
8870   HDC hdc;\r
8871   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8872 \r
8873   if(appData.noGUI) return;\r
8874   hdc = GetDC(hwndMain);\r
8875   if (!IsIconic(hwndMain)) {\r
8876     DisplayAClock(hdc, timeRemaining, highlight, \r
8877                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8878   }\r
8879   if (highlight && iconCurrent == iconBlack) {\r
8880     iconCurrent = iconWhite;\r
8881     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8882     if (IsIconic(hwndMain)) {\r
8883       DrawIcon(hdc, 2, 2, iconCurrent);\r
8884     }\r
8885   }\r
8886   (void) ReleaseDC(hwndMain, hdc);\r
8887   if (hwndConsole)\r
8888     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8889 }\r
8890 \r
8891 void\r
8892 DisplayBlackClock(long timeRemaining, int highlight)\r
8893 {\r
8894   HDC hdc;\r
8895   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8896 \r
8897 \r
8898   if(appData.noGUI) return;\r
8899   hdc = GetDC(hwndMain);\r
8900   if (!IsIconic(hwndMain)) {\r
8901     DisplayAClock(hdc, timeRemaining, highlight, \r
8902                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8903   }\r
8904   if (highlight && iconCurrent == iconWhite) {\r
8905     iconCurrent = iconBlack;\r
8906     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8907     if (IsIconic(hwndMain)) {\r
8908       DrawIcon(hdc, 2, 2, iconCurrent);\r
8909     }\r
8910   }\r
8911   (void) ReleaseDC(hwndMain, hdc);\r
8912   if (hwndConsole)\r
8913     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8914 }\r
8915 \r
8916 \r
8917 int\r
8918 LoadGameTimerRunning()\r
8919 {\r
8920   return loadGameTimerEvent != 0;\r
8921 }\r
8922 \r
8923 int\r
8924 StopLoadGameTimer()\r
8925 {\r
8926   if (loadGameTimerEvent == 0) return FALSE;\r
8927   KillTimer(hwndMain, loadGameTimerEvent);\r
8928   loadGameTimerEvent = 0;\r
8929   return TRUE;\r
8930 }\r
8931 \r
8932 void\r
8933 StartLoadGameTimer(long millisec)\r
8934 {\r
8935   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8936                                 (UINT) millisec, NULL);\r
8937 }\r
8938 \r
8939 void\r
8940 AutoSaveGame()\r
8941 {\r
8942   char *defName;\r
8943   FILE *f;\r
8944   char fileTitle[MSG_SIZ];\r
8945 \r
8946   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8947   f = OpenFileDialog(hwndMain, "a", defName,\r
8948                      appData.oldSaveStyle ? "gam" : "pgn",\r
8949                      GAME_FILT, \r
8950                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8951   if (f != NULL) {\r
8952     SaveGame(f, 0, "");\r
8953     fclose(f);\r
8954   }\r
8955 }\r
8956 \r
8957 \r
8958 void\r
8959 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8960 {\r
8961   if (delayedTimerEvent != 0) {\r
8962     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8963       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8964     }\r
8965     KillTimer(hwndMain, delayedTimerEvent);\r
8966     delayedTimerEvent = 0;\r
8967     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8968     delayedTimerCallback();\r
8969   }\r
8970   delayedTimerCallback = cb;\r
8971   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8972                                 (UINT) millisec, NULL);\r
8973 }\r
8974 \r
8975 DelayedEventCallback\r
8976 GetDelayedEvent()\r
8977 {\r
8978   if (delayedTimerEvent) {\r
8979     return delayedTimerCallback;\r
8980   } else {\r
8981     return NULL;\r
8982   }\r
8983 }\r
8984 \r
8985 void\r
8986 CancelDelayedEvent()\r
8987 {\r
8988   if (delayedTimerEvent) {\r
8989     KillTimer(hwndMain, delayedTimerEvent);\r
8990     delayedTimerEvent = 0;\r
8991   }\r
8992 }\r
8993 \r
8994 DWORD GetWin32Priority(int nice)\r
8995 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8996 /*\r
8997 REALTIME_PRIORITY_CLASS     0x00000100\r
8998 HIGH_PRIORITY_CLASS         0x00000080\r
8999 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9000 NORMAL_PRIORITY_CLASS       0x00000020\r
9001 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9002 IDLE_PRIORITY_CLASS         0x00000040\r
9003 */\r
9004         if (nice < -15) return 0x00000080;\r
9005         if (nice < 0)   return 0x00008000;\r
9006         if (nice == 0)  return 0x00000020;\r
9007         if (nice < 15)  return 0x00004000;\r
9008         return 0x00000040;\r
9009 }\r
9010 \r
9011 void RunCommand(char *cmdLine)\r
9012 {\r
9013   /* Now create the child process. */\r
9014   STARTUPINFO siStartInfo;\r
9015   PROCESS_INFORMATION piProcInfo;\r
9016 \r
9017   siStartInfo.cb = sizeof(STARTUPINFO);\r
9018   siStartInfo.lpReserved = NULL;\r
9019   siStartInfo.lpDesktop = NULL;\r
9020   siStartInfo.lpTitle = NULL;\r
9021   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9022   siStartInfo.cbReserved2 = 0;\r
9023   siStartInfo.lpReserved2 = NULL;\r
9024   siStartInfo.hStdInput = NULL;\r
9025   siStartInfo.hStdOutput = NULL;\r
9026   siStartInfo.hStdError = NULL;\r
9027 \r
9028   CreateProcess(NULL,\r
9029                 cmdLine,           /* command line */\r
9030                 NULL,      /* process security attributes */\r
9031                 NULL,      /* primary thread security attrs */\r
9032                 TRUE,      /* handles are inherited */\r
9033                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9034                 NULL,      /* use parent's environment */\r
9035                 NULL,\r
9036                 &siStartInfo, /* STARTUPINFO pointer */\r
9037                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9038 \r
9039   CloseHandle(piProcInfo.hThread);\r
9040 }\r
9041 \r
9042 /* Start a child process running the given program.\r
9043    The process's standard output can be read from "from", and its\r
9044    standard input can be written to "to".\r
9045    Exit with fatal error if anything goes wrong.\r
9046    Returns an opaque pointer that can be used to destroy the process\r
9047    later.\r
9048 */\r
9049 int\r
9050 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9051 {\r
9052 #define BUFSIZE 4096\r
9053 \r
9054   HANDLE hChildStdinRd, hChildStdinWr,\r
9055     hChildStdoutRd, hChildStdoutWr;\r
9056   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9057   SECURITY_ATTRIBUTES saAttr;\r
9058   BOOL fSuccess;\r
9059   PROCESS_INFORMATION piProcInfo;\r
9060   STARTUPINFO siStartInfo;\r
9061   ChildProc *cp;\r
9062   char buf[MSG_SIZ];\r
9063   DWORD err;\r
9064 \r
9065   if (appData.debugMode) {\r
9066     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9067   }\r
9068 \r
9069   *pr = NoProc;\r
9070 \r
9071   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9072   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9073   saAttr.bInheritHandle = TRUE;\r
9074   saAttr.lpSecurityDescriptor = NULL;\r
9075 \r
9076   /*\r
9077    * The steps for redirecting child's STDOUT:\r
9078    *     1. Create anonymous pipe to be STDOUT for child.\r
9079    *     2. Create a noninheritable duplicate of read handle,\r
9080    *         and close the inheritable read handle.\r
9081    */\r
9082 \r
9083   /* Create a pipe for the child's STDOUT. */\r
9084   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9085     return GetLastError();\r
9086   }\r
9087 \r
9088   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9089   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9090                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9091                              FALSE,     /* not inherited */\r
9092                              DUPLICATE_SAME_ACCESS);\r
9093   if (! fSuccess) {\r
9094     return GetLastError();\r
9095   }\r
9096   CloseHandle(hChildStdoutRd);\r
9097 \r
9098   /*\r
9099    * The steps for redirecting child's STDIN:\r
9100    *     1. Create anonymous pipe to be STDIN for child.\r
9101    *     2. Create a noninheritable duplicate of write handle,\r
9102    *         and close the inheritable write handle.\r
9103    */\r
9104 \r
9105   /* Create a pipe for the child's STDIN. */\r
9106   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9107     return GetLastError();\r
9108   }\r
9109 \r
9110   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9111   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9112                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9113                              FALSE,     /* not inherited */\r
9114                              DUPLICATE_SAME_ACCESS);\r
9115   if (! fSuccess) {\r
9116     return GetLastError();\r
9117   }\r
9118   CloseHandle(hChildStdinWr);\r
9119 \r
9120   /* Arrange to (1) look in dir for the child .exe file, and\r
9121    * (2) have dir be the child's working directory.  Interpret\r
9122    * dir relative to the directory WinBoard loaded from. */\r
9123   GetCurrentDirectory(MSG_SIZ, buf);\r
9124   SetCurrentDirectory(installDir);\r
9125   SetCurrentDirectory(dir);\r
9126 \r
9127   /* Now create the child process. */\r
9128 \r
9129   siStartInfo.cb = sizeof(STARTUPINFO);\r
9130   siStartInfo.lpReserved = NULL;\r
9131   siStartInfo.lpDesktop = NULL;\r
9132   siStartInfo.lpTitle = NULL;\r
9133   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9134   siStartInfo.cbReserved2 = 0;\r
9135   siStartInfo.lpReserved2 = NULL;\r
9136   siStartInfo.hStdInput = hChildStdinRd;\r
9137   siStartInfo.hStdOutput = hChildStdoutWr;\r
9138   siStartInfo.hStdError = hChildStdoutWr;\r
9139 \r
9140   fSuccess = CreateProcess(NULL,\r
9141                            cmdLine,        /* command line */\r
9142                            NULL,           /* process security attributes */\r
9143                            NULL,           /* primary thread security attrs */\r
9144                            TRUE,           /* handles are inherited */\r
9145                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9146                            NULL,           /* use parent's environment */\r
9147                            NULL,\r
9148                            &siStartInfo, /* STARTUPINFO pointer */\r
9149                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9150 \r
9151   err = GetLastError();\r
9152   SetCurrentDirectory(buf); /* return to prev directory */\r
9153   if (! fSuccess) {\r
9154     return err;\r
9155   }\r
9156 \r
9157   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9158     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9159     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9160   }\r
9161 \r
9162   /* Close the handles we don't need in the parent */\r
9163   CloseHandle(piProcInfo.hThread);\r
9164   CloseHandle(hChildStdinRd);\r
9165   CloseHandle(hChildStdoutWr);\r
9166 \r
9167   /* Prepare return value */\r
9168   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9169   cp->kind = CPReal;\r
9170   cp->hProcess = piProcInfo.hProcess;\r
9171   cp->pid = piProcInfo.dwProcessId;\r
9172   cp->hFrom = hChildStdoutRdDup;\r
9173   cp->hTo = hChildStdinWrDup;\r
9174 \r
9175   *pr = (void *) cp;\r
9176 \r
9177   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9178      2000 where engines sometimes don't see the initial command(s)\r
9179      from WinBoard and hang.  I don't understand how that can happen,\r
9180      but the Sleep is harmless, so I've put it in.  Others have also\r
9181      reported what may be the same problem, so hopefully this will fix\r
9182      it for them too.  */\r
9183   Sleep(500);\r
9184 \r
9185   return NO_ERROR;\r
9186 }\r
9187 \r
9188 \r
9189 void\r
9190 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9191 {\r
9192   ChildProc *cp; int result;\r
9193 \r
9194   cp = (ChildProc *) pr;\r
9195   if (cp == NULL) return;\r
9196 \r
9197   switch (cp->kind) {\r
9198   case CPReal:\r
9199     /* TerminateProcess is considered harmful, so... */\r
9200     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9201     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9202     /* The following doesn't work because the chess program\r
9203        doesn't "have the same console" as WinBoard.  Maybe\r
9204        we could arrange for this even though neither WinBoard\r
9205        nor the chess program uses a console for stdio? */\r
9206     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9207 \r
9208     /* [AS] Special termination modes for misbehaving programs... */\r
9209     if( signal == 9 ) { \r
9210         result = TerminateProcess( cp->hProcess, 0 );\r
9211 \r
9212         if ( appData.debugMode) {\r
9213             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9214         }\r
9215     }\r
9216     else if( signal == 10 ) {\r
9217         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9218 \r
9219         if( dw != WAIT_OBJECT_0 ) {\r
9220             result = TerminateProcess( cp->hProcess, 0 );\r
9221 \r
9222             if ( appData.debugMode) {\r
9223                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9224             }\r
9225 \r
9226         }\r
9227     }\r
9228 \r
9229     CloseHandle(cp->hProcess);\r
9230     break;\r
9231 \r
9232   case CPComm:\r
9233     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9234     break;\r
9235 \r
9236   case CPSock:\r
9237     closesocket(cp->sock);\r
9238     WSACleanup();\r
9239     break;\r
9240 \r
9241   case CPRcmd:\r
9242     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9243     closesocket(cp->sock);\r
9244     closesocket(cp->sock2);\r
9245     WSACleanup();\r
9246     break;\r
9247   }\r
9248   free(cp);\r
9249 }\r
9250 \r
9251 void\r
9252 InterruptChildProcess(ProcRef pr)\r
9253 {\r
9254   ChildProc *cp;\r
9255 \r
9256   cp = (ChildProc *) pr;\r
9257   if (cp == NULL) return;\r
9258   switch (cp->kind) {\r
9259   case CPReal:\r
9260     /* The following doesn't work because the chess program\r
9261        doesn't "have the same console" as WinBoard.  Maybe\r
9262        we could arrange for this even though neither WinBoard\r
9263        nor the chess program uses a console for stdio */\r
9264     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9265     break;\r
9266 \r
9267   case CPComm:\r
9268   case CPSock:\r
9269     /* Can't interrupt */\r
9270     break;\r
9271 \r
9272   case CPRcmd:\r
9273     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9274     break;\r
9275   }\r
9276 }\r
9277 \r
9278 \r
9279 int\r
9280 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9281 {\r
9282   char cmdLine[MSG_SIZ];\r
9283 \r
9284   if (port[0] == NULLCHAR) {\r
9285     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9286   } else {\r
9287     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9288   }\r
9289   return StartChildProcess(cmdLine, "", pr);\r
9290 }\r
9291 \r
9292 \r
9293 /* Code to open TCP sockets */\r
9294 \r
9295 int\r
9296 OpenTCP(char *host, char *port, ProcRef *pr)\r
9297 {\r
9298   ChildProc *cp;\r
9299   int err;\r
9300   SOCKET s;\r
9301 \r
9302   struct sockaddr_in sa, mysa;\r
9303   struct hostent FAR *hp;\r
9304   unsigned short uport;\r
9305   WORD wVersionRequested;\r
9306   WSADATA wsaData;\r
9307 \r
9308   /* Initialize socket DLL */\r
9309   wVersionRequested = MAKEWORD(1, 1);\r
9310   err = WSAStartup(wVersionRequested, &wsaData);\r
9311   if (err != 0) return err;\r
9312 \r
9313   /* Make socket */\r
9314   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9315     err = WSAGetLastError();\r
9316     WSACleanup();\r
9317     return err;\r
9318   }\r
9319 \r
9320   /* Bind local address using (mostly) don't-care values.\r
9321    */\r
9322   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9323   mysa.sin_family = AF_INET;\r
9324   mysa.sin_addr.s_addr = INADDR_ANY;\r
9325   uport = (unsigned short) 0;\r
9326   mysa.sin_port = htons(uport);\r
9327   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9328       == SOCKET_ERROR) {\r
9329     err = WSAGetLastError();\r
9330     WSACleanup();\r
9331     return err;\r
9332   }\r
9333 \r
9334   /* Resolve remote host name */\r
9335   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9336   if (!(hp = gethostbyname(host))) {\r
9337     unsigned int b0, b1, b2, b3;\r
9338 \r
9339     err = WSAGetLastError();\r
9340 \r
9341     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9342       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9343       hp->h_addrtype = AF_INET;\r
9344       hp->h_length = 4;\r
9345       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9346       hp->h_addr_list[0] = (char *) malloc(4);\r
9347       hp->h_addr_list[0][0] = (char) b0;\r
9348       hp->h_addr_list[0][1] = (char) b1;\r
9349       hp->h_addr_list[0][2] = (char) b2;\r
9350       hp->h_addr_list[0][3] = (char) b3;\r
9351     } else {\r
9352       WSACleanup();\r
9353       return err;\r
9354     }\r
9355   }\r
9356   sa.sin_family = hp->h_addrtype;\r
9357   uport = (unsigned short) atoi(port);\r
9358   sa.sin_port = htons(uport);\r
9359   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9360 \r
9361   /* Make connection */\r
9362   if (connect(s, (struct sockaddr *) &sa,\r
9363               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9364     err = WSAGetLastError();\r
9365     WSACleanup();\r
9366     return err;\r
9367   }\r
9368 \r
9369   /* Prepare return value */\r
9370   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9371   cp->kind = CPSock;\r
9372   cp->sock = s;\r
9373   *pr = (ProcRef *) cp;\r
9374 \r
9375   return NO_ERROR;\r
9376 }\r
9377 \r
9378 int\r
9379 OpenCommPort(char *name, ProcRef *pr)\r
9380 {\r
9381   HANDLE h;\r
9382   COMMTIMEOUTS ct;\r
9383   ChildProc *cp;\r
9384   char fullname[MSG_SIZ];\r
9385 \r
9386   if (*name != '\\')\r
9387     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9388   else\r
9389     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9390 \r
9391   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9392                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9393   if (h == (HANDLE) -1) {\r
9394     return GetLastError();\r
9395   }\r
9396   hCommPort = h;\r
9397 \r
9398   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9399 \r
9400   /* Accumulate characters until a 100ms pause, then parse */\r
9401   ct.ReadIntervalTimeout = 100;\r
9402   ct.ReadTotalTimeoutMultiplier = 0;\r
9403   ct.ReadTotalTimeoutConstant = 0;\r
9404   ct.WriteTotalTimeoutMultiplier = 0;\r
9405   ct.WriteTotalTimeoutConstant = 0;\r
9406   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9407 \r
9408   /* Prepare return value */\r
9409   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9410   cp->kind = CPComm;\r
9411   cp->hFrom = h;\r
9412   cp->hTo = h;\r
9413   *pr = (ProcRef *) cp;\r
9414 \r
9415   return NO_ERROR;\r
9416 }\r
9417 \r
9418 int\r
9419 OpenLoopback(ProcRef *pr)\r
9420 {\r
9421   DisplayFatalError(_("Not implemented"), 0, 1);\r
9422   return NO_ERROR;\r
9423 }\r
9424 \r
9425 \r
9426 int\r
9427 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9428 {\r
9429   ChildProc *cp;\r
9430   int err;\r
9431   SOCKET s, s2, s3;\r
9432   struct sockaddr_in sa, mysa;\r
9433   struct hostent FAR *hp;\r
9434   unsigned short uport;\r
9435   WORD wVersionRequested;\r
9436   WSADATA wsaData;\r
9437   int fromPort;\r
9438   char stderrPortStr[MSG_SIZ];\r
9439 \r
9440   /* Initialize socket DLL */\r
9441   wVersionRequested = MAKEWORD(1, 1);\r
9442   err = WSAStartup(wVersionRequested, &wsaData);\r
9443   if (err != 0) return err;\r
9444 \r
9445   /* Resolve remote host name */\r
9446   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9447   if (!(hp = gethostbyname(host))) {\r
9448     unsigned int b0, b1, b2, b3;\r
9449 \r
9450     err = WSAGetLastError();\r
9451 \r
9452     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9453       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9454       hp->h_addrtype = AF_INET;\r
9455       hp->h_length = 4;\r
9456       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9457       hp->h_addr_list[0] = (char *) malloc(4);\r
9458       hp->h_addr_list[0][0] = (char) b0;\r
9459       hp->h_addr_list[0][1] = (char) b1;\r
9460       hp->h_addr_list[0][2] = (char) b2;\r
9461       hp->h_addr_list[0][3] = (char) b3;\r
9462     } else {\r
9463       WSACleanup();\r
9464       return err;\r
9465     }\r
9466   }\r
9467   sa.sin_family = hp->h_addrtype;\r
9468   uport = (unsigned short) 514;\r
9469   sa.sin_port = htons(uport);\r
9470   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9471 \r
9472   /* Bind local socket to unused "privileged" port address\r
9473    */\r
9474   s = INVALID_SOCKET;\r
9475   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9476   mysa.sin_family = AF_INET;\r
9477   mysa.sin_addr.s_addr = INADDR_ANY;\r
9478   for (fromPort = 1023;; fromPort--) {\r
9479     if (fromPort < 0) {\r
9480       WSACleanup();\r
9481       return WSAEADDRINUSE;\r
9482     }\r
9483     if (s == INVALID_SOCKET) {\r
9484       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9485         err = WSAGetLastError();\r
9486         WSACleanup();\r
9487         return err;\r
9488       }\r
9489     }\r
9490     uport = (unsigned short) fromPort;\r
9491     mysa.sin_port = htons(uport);\r
9492     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9493         == SOCKET_ERROR) {\r
9494       err = WSAGetLastError();\r
9495       if (err == WSAEADDRINUSE) continue;\r
9496       WSACleanup();\r
9497       return err;\r
9498     }\r
9499     if (connect(s, (struct sockaddr *) &sa,\r
9500       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9501       err = WSAGetLastError();\r
9502       if (err == WSAEADDRINUSE) {\r
9503         closesocket(s);\r
9504         s = -1;\r
9505         continue;\r
9506       }\r
9507       WSACleanup();\r
9508       return err;\r
9509     }\r
9510     break;\r
9511   }\r
9512 \r
9513   /* Bind stderr local socket to unused "privileged" port address\r
9514    */\r
9515   s2 = INVALID_SOCKET;\r
9516   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9517   mysa.sin_family = AF_INET;\r
9518   mysa.sin_addr.s_addr = INADDR_ANY;\r
9519   for (fromPort = 1023;; fromPort--) {\r
9520     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9521     if (fromPort < 0) {\r
9522       (void) closesocket(s);\r
9523       WSACleanup();\r
9524       return WSAEADDRINUSE;\r
9525     }\r
9526     if (s2 == INVALID_SOCKET) {\r
9527       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9528         err = WSAGetLastError();\r
9529         closesocket(s);\r
9530         WSACleanup();\r
9531         return err;\r
9532       }\r
9533     }\r
9534     uport = (unsigned short) fromPort;\r
9535     mysa.sin_port = htons(uport);\r
9536     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9537         == SOCKET_ERROR) {\r
9538       err = WSAGetLastError();\r
9539       if (err == WSAEADDRINUSE) continue;\r
9540       (void) closesocket(s);\r
9541       WSACleanup();\r
9542       return err;\r
9543     }\r
9544     if (listen(s2, 1) == SOCKET_ERROR) {\r
9545       err = WSAGetLastError();\r
9546       if (err == WSAEADDRINUSE) {\r
9547         closesocket(s2);\r
9548         s2 = INVALID_SOCKET;\r
9549         continue;\r
9550       }\r
9551       (void) closesocket(s);\r
9552       (void) closesocket(s2);\r
9553       WSACleanup();\r
9554       return err;\r
9555     }\r
9556     break;\r
9557   }\r
9558   prevStderrPort = fromPort; // remember port used\r
9559   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9560 \r
9561   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9562     err = WSAGetLastError();\r
9563     (void) closesocket(s);\r
9564     (void) closesocket(s2);\r
9565     WSACleanup();\r
9566     return err;\r
9567   }\r
9568 \r
9569   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9570     err = WSAGetLastError();\r
9571     (void) closesocket(s);\r
9572     (void) closesocket(s2);\r
9573     WSACleanup();\r
9574     return err;\r
9575   }\r
9576   if (*user == NULLCHAR) user = UserName();\r
9577   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9578     err = WSAGetLastError();\r
9579     (void) closesocket(s);\r
9580     (void) closesocket(s2);\r
9581     WSACleanup();\r
9582     return err;\r
9583   }\r
9584   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9585     err = WSAGetLastError();\r
9586     (void) closesocket(s);\r
9587     (void) closesocket(s2);\r
9588     WSACleanup();\r
9589     return err;\r
9590   }\r
9591 \r
9592   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9593     err = WSAGetLastError();\r
9594     (void) closesocket(s);\r
9595     (void) closesocket(s2);\r
9596     WSACleanup();\r
9597     return err;\r
9598   }\r
9599   (void) closesocket(s2);  /* Stop listening */\r
9600 \r
9601   /* Prepare return value */\r
9602   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9603   cp->kind = CPRcmd;\r
9604   cp->sock = s;\r
9605   cp->sock2 = s3;\r
9606   *pr = (ProcRef *) cp;\r
9607 \r
9608   return NO_ERROR;\r
9609 }\r
9610 \r
9611 \r
9612 InputSourceRef\r
9613 AddInputSource(ProcRef pr, int lineByLine,\r
9614                InputCallback func, VOIDSTAR closure)\r
9615 {\r
9616   InputSource *is, *is2 = NULL;\r
9617   ChildProc *cp = (ChildProc *) pr;\r
9618 \r
9619   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9620   is->lineByLine = lineByLine;\r
9621   is->func = func;\r
9622   is->closure = closure;\r
9623   is->second = NULL;\r
9624   is->next = is->buf;\r
9625   if (pr == NoProc) {\r
9626     is->kind = CPReal;\r
9627     consoleInputSource = is;\r
9628   } else {\r
9629     is->kind = cp->kind;\r
9630     /* \r
9631         [AS] Try to avoid a race condition if the thread is given control too early:\r
9632         we create all threads suspended so that the is->hThread variable can be\r
9633         safely assigned, then let the threads start with ResumeThread.\r
9634     */\r
9635     switch (cp->kind) {\r
9636     case CPReal:\r
9637       is->hFile = cp->hFrom;\r
9638       cp->hFrom = NULL; /* now owned by InputThread */\r
9639       is->hThread =\r
9640         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9641                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9642       break;\r
9643 \r
9644     case CPComm:\r
9645       is->hFile = cp->hFrom;\r
9646       cp->hFrom = NULL; /* now owned by InputThread */\r
9647       is->hThread =\r
9648         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9649                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9650       break;\r
9651 \r
9652     case CPSock:\r
9653       is->sock = cp->sock;\r
9654       is->hThread =\r
9655         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9656                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9657       break;\r
9658 \r
9659     case CPRcmd:\r
9660       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9661       *is2 = *is;\r
9662       is->sock = cp->sock;\r
9663       is->second = is2;\r
9664       is2->sock = cp->sock2;\r
9665       is2->second = is2;\r
9666       is->hThread =\r
9667         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9668                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9669       is2->hThread =\r
9670         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9671                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9672       break;\r
9673     }\r
9674 \r
9675     if( is->hThread != NULL ) {\r
9676         ResumeThread( is->hThread );\r
9677     }\r
9678 \r
9679     if( is2 != NULL && is2->hThread != NULL ) {\r
9680         ResumeThread( is2->hThread );\r
9681     }\r
9682   }\r
9683 \r
9684   return (InputSourceRef) is;\r
9685 }\r
9686 \r
9687 void\r
9688 RemoveInputSource(InputSourceRef isr)\r
9689 {\r
9690   InputSource *is;\r
9691 \r
9692   is = (InputSource *) isr;\r
9693   is->hThread = NULL;  /* tell thread to stop */\r
9694   CloseHandle(is->hThread);\r
9695   if (is->second != NULL) {\r
9696     is->second->hThread = NULL;\r
9697     CloseHandle(is->second->hThread);\r
9698   }\r
9699 }\r
9700 \r
9701 int no_wrap(char *message, int count)\r
9702 {\r
9703     ConsoleOutput(message, count, FALSE);\r
9704     return count;\r
9705 }\r
9706 \r
9707 int\r
9708 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9709 {\r
9710   DWORD dOutCount;\r
9711   int outCount = SOCKET_ERROR;\r
9712   ChildProc *cp = (ChildProc *) pr;\r
9713   static OVERLAPPED ovl;\r
9714   static int line = 0;\r
9715 \r
9716   if (pr == NoProc)\r
9717   {\r
9718     if (appData.noJoin || !appData.useInternalWrap)\r
9719       return no_wrap(message, count);\r
9720     else\r
9721     {\r
9722       int width = get_term_width();\r
9723       int len = wrap(NULL, message, count, width, &line);\r
9724       char *msg = malloc(len);\r
9725       int dbgchk;\r
9726 \r
9727       if (!msg)\r
9728         return no_wrap(message, count);\r
9729       else\r
9730       {\r
9731         dbgchk = wrap(msg, message, count, width, &line);\r
9732         if (dbgchk != len && appData.debugMode)\r
9733             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9734         ConsoleOutput(msg, len, FALSE);\r
9735         free(msg);\r
9736         return len;\r
9737       }\r
9738     }\r
9739   }\r
9740 \r
9741   if (ovl.hEvent == NULL) {\r
9742     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9743   }\r
9744   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9745 \r
9746   switch (cp->kind) {\r
9747   case CPSock:\r
9748   case CPRcmd:\r
9749     outCount = send(cp->sock, message, count, 0);\r
9750     if (outCount == SOCKET_ERROR) {\r
9751       *outError = WSAGetLastError();\r
9752     } else {\r
9753       *outError = NO_ERROR;\r
9754     }\r
9755     break;\r
9756 \r
9757   case CPReal:\r
9758     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9759                   &dOutCount, NULL)) {\r
9760       *outError = NO_ERROR;\r
9761       outCount = (int) dOutCount;\r
9762     } else {\r
9763       *outError = GetLastError();\r
9764     }\r
9765     break;\r
9766 \r
9767   case CPComm:\r
9768     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9769                             &dOutCount, &ovl);\r
9770     if (*outError == NO_ERROR) {\r
9771       outCount = (int) dOutCount;\r
9772     }\r
9773     break;\r
9774   }\r
9775   return outCount;\r
9776 }\r
9777 \r
9778 void\r
9779 DoSleep(int n)\r
9780 {\r
9781     if(n != 0) Sleep(n);\r
9782 }\r
9783 \r
9784 int\r
9785 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9786                        long msdelay)\r
9787 {\r
9788   /* Ignore delay, not implemented for WinBoard */\r
9789   return OutputToProcess(pr, message, count, outError);\r
9790 }\r
9791 \r
9792 \r
9793 void\r
9794 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9795                         char *buf, int count, int error)\r
9796 {\r
9797   DisplayFatalError(_("Not implemented"), 0, 1);\r
9798 }\r
9799 \r
9800 /* see wgamelist.c for Game List functions */\r
9801 /* see wedittags.c for Edit Tags functions */\r
9802 \r
9803 \r
9804 int\r
9805 ICSInitScript()\r
9806 {\r
9807   FILE *f;\r
9808   char buf[MSG_SIZ];\r
9809   char *dummy;\r
9810 \r
9811   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9812     f = fopen(buf, "r");\r
9813     if (f != NULL) {\r
9814       ProcessICSInitScript(f);\r
9815       fclose(f);\r
9816       return TRUE;\r
9817     }\r
9818   }\r
9819   return FALSE;\r
9820 }\r
9821 \r
9822 \r
9823 VOID\r
9824 StartAnalysisClock()\r
9825 {\r
9826   if (analysisTimerEvent) return;\r
9827   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9828                                         (UINT) 2000, NULL);\r
9829 }\r
9830 \r
9831 VOID\r
9832 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9833 {\r
9834   highlightInfo.sq[0].x = fromX;\r
9835   highlightInfo.sq[0].y = fromY;\r
9836   highlightInfo.sq[1].x = toX;\r
9837   highlightInfo.sq[1].y = toY;\r
9838 }\r
9839 \r
9840 VOID\r
9841 ClearHighlights()\r
9842 {\r
9843   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9844     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9845 }\r
9846 \r
9847 VOID\r
9848 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9849 {\r
9850   premoveHighlightInfo.sq[0].x = fromX;\r
9851   premoveHighlightInfo.sq[0].y = fromY;\r
9852   premoveHighlightInfo.sq[1].x = toX;\r
9853   premoveHighlightInfo.sq[1].y = toY;\r
9854 }\r
9855 \r
9856 VOID\r
9857 ClearPremoveHighlights()\r
9858 {\r
9859   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9860     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9861 }\r
9862 \r
9863 VOID\r
9864 ShutDownFrontEnd()\r
9865 {\r
9866   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9867   DeleteClipboardTempFiles();\r
9868 }\r
9869 \r
9870 void\r
9871 BoardToTop()\r
9872 {\r
9873     if (IsIconic(hwndMain))\r
9874       ShowWindow(hwndMain, SW_RESTORE);\r
9875 \r
9876     SetActiveWindow(hwndMain);\r
9877 }\r
9878 \r
9879 /*\r
9880  * Prototypes for animation support routines\r
9881  */\r
9882 static void ScreenSquare(int column, int row, POINT * pt);\r
9883 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9884      POINT frames[], int * nFrames);\r
9885 \r
9886 \r
9887 #define kFactor 4\r
9888 \r
9889 void\r
9890 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9891 {       // [HGM] atomic: animate blast wave\r
9892         int i;\r
9893 \r
9894         explodeInfo.fromX = fromX;\r
9895         explodeInfo.fromY = fromY;\r
9896         explodeInfo.toX = toX;\r
9897         explodeInfo.toY = toY;\r
9898         for(i=1; i<4*kFactor; i++) {\r
9899             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9900             DrawPosition(FALSE, board);\r
9901             Sleep(appData.animSpeed);\r
9902         }\r
9903         explodeInfo.radius = 0;\r
9904         DrawPosition(TRUE, board);\r
9905 }\r
9906 \r
9907 void\r
9908 AnimateMove(board, fromX, fromY, toX, toY)\r
9909      Board board;\r
9910      int fromX;\r
9911      int fromY;\r
9912      int toX;\r
9913      int toY;\r
9914 {\r
9915   ChessSquare piece;\r
9916   int x = toX, y = toY;\r
9917   POINT start, finish, mid;\r
9918   POINT frames[kFactor * 2 + 1];\r
9919   int nFrames, n;\r
9920 \r
9921   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9922 \r
9923   if (!appData.animate) return;\r
9924   if (doingSizing) return;\r
9925   if (fromY < 0 || fromX < 0) return;\r
9926   piece = board[fromY][fromX];\r
9927   if (piece >= EmptySquare) return;\r
9928 \r
9929   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
9930 \r
9931 again:\r
9932 \r
9933   ScreenSquare(fromX, fromY, &start);\r
9934   ScreenSquare(toX, toY, &finish);\r
9935 \r
9936   /* All moves except knight jumps move in straight line */\r
9937   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9938     mid.x = start.x + (finish.x - start.x) / 2;\r
9939     mid.y = start.y + (finish.y - start.y) / 2;\r
9940   } else {\r
9941     /* Knight: make straight movement then diagonal */\r
9942     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9943        mid.x = start.x + (finish.x - start.x) / 2;\r
9944        mid.y = start.y;\r
9945      } else {\r
9946        mid.x = start.x;\r
9947        mid.y = start.y + (finish.y - start.y) / 2;\r
9948      }\r
9949   }\r
9950   \r
9951   /* Don't use as many frames for very short moves */\r
9952   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9953     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9954   else\r
9955     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9956 \r
9957   animInfo.from.x = fromX;\r
9958   animInfo.from.y = fromY;\r
9959   animInfo.to.x = toX;\r
9960   animInfo.to.y = toY;\r
9961   animInfo.lastpos = start;\r
9962   animInfo.piece = piece;\r
9963   for (n = 0; n < nFrames; n++) {\r
9964     animInfo.pos = frames[n];\r
9965     DrawPosition(FALSE, NULL);\r
9966     animInfo.lastpos = animInfo.pos;\r
9967     Sleep(appData.animSpeed);\r
9968   }\r
9969   animInfo.pos = finish;\r
9970   DrawPosition(FALSE, NULL);\r
9971 \r
9972   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
9973 \r
9974   animInfo.piece = EmptySquare;\r
9975   Explode(board, fromX, fromY, toX, toY);\r
9976 }\r
9977 \r
9978 /*      Convert board position to corner of screen rect and color       */\r
9979 \r
9980 static void\r
9981 ScreenSquare(column, row, pt)\r
9982      int column; int row; POINT * pt;\r
9983 {\r
9984   if (flipView) {\r
9985     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9986     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9987   } else {\r
9988     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9989     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9990   }\r
9991 }\r
9992 \r
9993 /*      Generate a series of frame coords from start->mid->finish.\r
9994         The movement rate doubles until the half way point is\r
9995         reached, then halves back down to the final destination,\r
9996         which gives a nice slow in/out effect. The algorithmn\r
9997         may seem to generate too many intermediates for short\r
9998         moves, but remember that the purpose is to attract the\r
9999         viewers attention to the piece about to be moved and\r
10000         then to where it ends up. Too few frames would be less\r
10001         noticeable.                                             */\r
10002 \r
10003 static void\r
10004 Tween(start, mid, finish, factor, frames, nFrames)\r
10005      POINT * start; POINT * mid;\r
10006      POINT * finish; int factor;\r
10007      POINT frames[]; int * nFrames;\r
10008 {\r
10009   int n, fraction = 1, count = 0;\r
10010 \r
10011   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10012   for (n = 0; n < factor; n++)\r
10013     fraction *= 2;\r
10014   for (n = 0; n < factor; n++) {\r
10015     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10016     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10017     count ++;\r
10018     fraction = fraction / 2;\r
10019   }\r
10020   \r
10021   /* Midpoint */\r
10022   frames[count] = *mid;\r
10023   count ++;\r
10024   \r
10025   /* Slow out, stepping 1/2, then 1/4, ... */\r
10026   fraction = 2;\r
10027   for (n = 0; n < factor; n++) {\r
10028     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10029     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10030     count ++;\r
10031     fraction = fraction * 2;\r
10032   }\r
10033   *nFrames = count;\r
10034 }\r
10035 \r
10036 void\r
10037 SettingsPopUp(ChessProgramState *cps)\r
10038 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10039       EngineOptionsPopup(savedHwnd, cps);\r
10040 }\r
10041 \r
10042 int flock(int fid, int code)\r
10043 {\r
10044     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10045     OVERLAPPED ov;\r
10046     ov.hEvent = NULL;\r
10047     ov.Offset = 0;\r
10048     ov.OffsetHigh = 0;\r
10049     switch(code) {\r
10050       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10051       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10052       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10053       default: return -1;\r
10054     }\r
10055     return 0;\r
10056 }\r
10057 \r
10058 char *\r
10059 Col2Text (int n)\r
10060 {\r
10061     static int i=0;\r
10062     static char col[8][20];\r
10063     COLORREF color = *(COLORREF *) colorVariable[n];\r
10064     i = i+1 & 7;\r
10065     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10066     return col[i];\r
10067 }\r
10068 \r
10069 void\r
10070 ActivateTheme (int new)\r
10071 {   // Redo initialization of features depending on options that can occur in themes\r
10072    InitTextures();\r
10073    if(new) InitDrawingColors();\r
10074    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10075    InitDrawingSizes(boardSize, 0);\r
10076    InvalidateRect(hwndMain, NULL, TRUE);\r
10077 }\r