Fix initial board sizing WB
[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 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern enum ICS_TYPE ics_type;\r
104 \r
105 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P((char *s));\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129   ChessSquare piece;\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
142 \r
143 typedef struct { // [HGM] atomic\r
144   int fromX, fromY, toX, toY, radius;\r
145 } ExplodeInfo;\r
146 \r
147 static ExplodeInfo explodeInfo;\r
148 \r
149 /* Window class names */\r
150 char szAppName[] = "WinBoard";\r
151 char szConsoleName[] = "WBConsole";\r
152 \r
153 /* Title bar text */\r
154 char szTitle[] = "WinBoard";\r
155 char szConsoleTitle[] = "I C S Interaction";\r
156 \r
157 char *programName;\r
158 char *settingsFileName;\r
159 Boolean saveSettingsOnExit;\r
160 char installDir[MSG_SIZ];\r
161 int errorExitStatus;\r
162 \r
163 BoardSize boardSize;\r
164 Boolean chessProgram;\r
165 //static int boardX, boardY;\r
166 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
167 int squareSize, lineGap, minorSize, border;\r
168 static int winW, winH;\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
170 static int logoHeight = 0;\r
171 static char messageText[MESSAGE_TEXT_MAX];\r
172 static int clockTimerEvent = 0;\r
173 static int loadGameTimerEvent = 0;\r
174 static int analysisTimerEvent = 0;\r
175 static DelayedEventCallback delayedTimerCallback;\r
176 static int delayedTimerEvent = 0;\r
177 static int buttonCount = 2;\r
178 char *icsTextMenuString;\r
179 char *icsNames;\r
180 char *firstChessProgramNames;\r
181 char *secondChessProgramNames;\r
182 \r
183 #define PALETTESIZE 256\r
184 \r
185 HINSTANCE hInst;          /* current instance */\r
186 Boolean alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush,      /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 \r
226 #if defined(_winmajor)\r
227 #define oldDialog (_winmajor < 4)\r
228 #else\r
229 #define oldDialog 0\r
230 #endif\r
231 #endif\r
232 \r
233 #define INTERNATIONAL\r
234 \r
235 #ifdef INTERNATIONAL\r
236 #  define _(s) T_(s)\r
237 #  define N_(s) s\r
238 #else\r
239 #  define _(s) s\r
240 #  define N_(s) s\r
241 #  define T_(s) s\r
242 #  define Translate(x, y)\r
243 #  define LoadLanguageFile(s)\r
244 #endif\r
245 \r
246 #ifdef INTERNATIONAL\r
247 \r
248 Boolean barbaric; // flag indicating if translation is needed\r
249 \r
250 // list of item numbers used in each dialog (used to alter language at run time)\r
251 \r
252 #define ABOUTBOX -1  /* not sure why these are needed */\r
253 #define ABOUTBOX2 -1\r
254 \r
255 int dialogItems[][42] = {\r
256 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
257 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
258   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
259 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
260   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
261 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
262   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
263 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
264 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
265   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
266 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
267 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
268   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
269 { ABOUTBOX2, IDC_ChessBoard }, \r
270 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
271   OPT_GameListClose, IDC_GameListDoFilter }, \r
272 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
273 { DLG_Error, IDOK }, \r
274 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
275   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
276 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
277 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
278   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
279   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
280 { DLG_IndexNumber, IDC_Index }, \r
281 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
282 { DLG_TypeInName, IDOK, IDCANCEL }, \r
283 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
284   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
285 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
286   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
287   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
288   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
289   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
290   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
291   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
292 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
293   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
294   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
295   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
296   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
297   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
298   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
299   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
300   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
301 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
302   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
303   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
304   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
305   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
306   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
307   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
308   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
309 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
310   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
311   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
312   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
313   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
314   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
315   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
316   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
317   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
318 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
319   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
320   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
321   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
322   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
323 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
324 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
325   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
326 { DLG_MoveHistory }, \r
327 { DLG_EvalGraph }, \r
328 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
329 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
330 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
331   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
332   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
333   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
334 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
335   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
336   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
337 { 0 }\r
338 };\r
339 \r
340 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
341 static int lastChecked;\r
342 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
343 extern int tinyLayout;\r
344 extern char * menuBarText[][10];\r
345 \r
346 void\r
347 LoadLanguageFile(char *name)\r
348 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
349     FILE *f;\r
350     int i=0, j=0, n=0, k;\r
351     char buf[MSG_SIZ];\r
352 \r
353     if(!name || name[0] == NULLCHAR) return;\r
354       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
355     appData.language = oldLanguage;\r
356     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
357     if((f = fopen(buf, "r")) == NULL) return;\r
358     while((k = fgetc(f)) != EOF) {\r
359         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
360         languageBuf[i] = k;\r
361         if(k == '\n') {\r
362             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
363                 char *p;\r
364                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
365                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
366                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
367                         english[j] = languageBuf + n + 1; *p = 0;\r
368                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
369 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
370                     }\r
371                 }\r
372             }\r
373             n = i + 1;\r
374         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
375             switch(k) {\r
376               case 'n': k = '\n'; break;\r
377               case 'r': k = '\r'; break;\r
378               case 't': k = '\t'; break;\r
379             }\r
380             languageBuf[--i] = k;\r
381         }\r
382         i++;\r
383     }\r
384     fclose(f);\r
385     barbaric = (j != 0);\r
386     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
387 }\r
388 \r
389 char *\r
390 T_(char *s)\r
391 {   // return the translation of the given string\r
392     // efficiency can be improved a lot...\r
393     int i=0;\r
394     static char buf[MSG_SIZ];\r
395 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
396     if(!barbaric) return s;\r
397     if(!s) return ""; // sanity\r
398     while(english[i]) {\r
399         if(!strcmp(s, english[i])) return foreign[i];\r
400         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
401             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
402             return buf;\r
403         }\r
404         i++;\r
405     }\r
406     return s;\r
407 }\r
408 \r
409 void\r
410 Translate(HWND hDlg, int dialogID)\r
411 {   // translate all text items in the given dialog\r
412     int i=0, j, k;\r
413     char buf[MSG_SIZ], *s;\r
414     if(!barbaric) return;\r
415     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
416     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
417     GetWindowText( hDlg, buf, MSG_SIZ );\r
418     s = T_(buf);\r
419     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
420     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
421         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
422         if(strlen(buf) == 0) continue;\r
423         s = T_(buf);\r
424         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
425     }\r
426 }\r
427 \r
428 HMENU\r
429 TranslateOneMenu(int i, HMENU subMenu)\r
430 {\r
431     int j;\r
432     static MENUITEMINFO info;\r
433 \r
434     info.cbSize = sizeof(MENUITEMINFO);\r
435     info.fMask = MIIM_STATE | MIIM_TYPE;\r
436           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
437             char buf[MSG_SIZ];\r
438             info.dwTypeData = buf;\r
439             info.cch = sizeof(buf);\r
440             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
441             if(i < 10) {\r
442                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
443                 else menuText[i][j] = strdup(buf); // remember original on first change\r
444             }\r
445             if(buf[0] == NULLCHAR) continue;\r
446             info.dwTypeData = T_(buf);\r
447             info.cch = strlen(buf)+1;\r
448             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
449           }\r
450     return subMenu;\r
451 }\r
452 \r
453 void\r
454 TranslateMenus(int addLanguage)\r
455 {\r
456     int i;\r
457     WIN32_FIND_DATA fileData;\r
458     HANDLE hFind;\r
459 #define IDM_English 1970\r
460     if(1) {\r
461         HMENU mainMenu = GetMenu(hwndMain);\r
462         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
463           HMENU subMenu = GetSubMenu(mainMenu, i);\r
464           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
465                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
466           TranslateOneMenu(i, subMenu);\r
467         }\r
468         DrawMenuBar(hwndMain);\r
469     }\r
470 \r
471     if(!addLanguage) return;\r
472     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
473         HMENU mainMenu = GetMenu(hwndMain);\r
474         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
475         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
476         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
477         i = 0; lastChecked = IDM_English;\r
478         do {\r
479             char *p, *q = fileData.cFileName;\r
480             int checkFlag = MF_UNCHECKED;\r
481             languageFile[i] = strdup(q);\r
482             if(barbaric && !strcmp(oldLanguage, q)) {\r
483                 checkFlag = MF_CHECKED;\r
484                 lastChecked = IDM_English + i + 1;\r
485                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
486             }\r
487             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
488             p = strstr(fileData.cFileName, ".lng");\r
489             if(p) *p = 0;\r
490             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
491         } while(FindNextFile(hFind, &fileData));\r
492         FindClose(hFind);\r
493     }\r
494 }\r
495 \r
496 #endif\r
497 \r
498 #define IDM_RecentEngines 3000\r
499 \r
500 void\r
501 RecentEngineMenu (char *s)\r
502 {\r
503     if(appData.icsActive) return;\r
504     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
505         HMENU mainMenu = GetMenu(hwndMain);\r
506         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
507         int i=IDM_RecentEngines;\r
508         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
509         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
510         while(*s) {\r
511           char *p = strchr(s, '\n');\r
512           if(p == NULL) return; // malformed!\r
513           *p = NULLCHAR;\r
514           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
515           *p = '\n';\r
516           s = p+1;\r
517         }\r
518     }\r
519 }\r
520 \r
521 \r
522 typedef struct {\r
523   char *name;\r
524   int squareSize;\r
525   int lineGap;\r
526   int smallLayout;\r
527   int tinyLayout;\r
528   int cliWidth, cliHeight;\r
529 } SizeInfo;\r
530 \r
531 SizeInfo sizeInfo[] = \r
532 {\r
533   { "tiny",     21, 0, 1, 1, 0, 0 },\r
534   { "teeny",    25, 1, 1, 1, 0, 0 },\r
535   { "dinky",    29, 1, 1, 1, 0, 0 },\r
536   { "petite",   33, 1, 1, 1, 0, 0 },\r
537   { "slim",     37, 2, 1, 0, 0, 0 },\r
538   { "small",    40, 2, 1, 0, 0, 0 },\r
539   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
540   { "middling", 49, 2, 0, 0, 0, 0 },\r
541   { "average",  54, 2, 0, 0, 0, 0 },\r
542   { "moderate", 58, 3, 0, 0, 0, 0 },\r
543   { "medium",   64, 3, 0, 0, 0, 0 },\r
544   { "bulky",    72, 3, 0, 0, 0, 0 },\r
545   { "large",    80, 3, 0, 0, 0, 0 },\r
546   { "big",      87, 3, 0, 0, 0, 0 },\r
547   { "huge",     95, 3, 0, 0, 0, 0 },\r
548   { "giant",    108, 3, 0, 0, 0, 0 },\r
549   { "colossal", 116, 4, 0, 0, 0, 0 },\r
550   { "titanic",  129, 4, 0, 0, 0, 0 },\r
551   { NULL, 0, 0, 0, 0, 0, 0 }\r
552 };\r
553 \r
554 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
555 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
556 {\r
557   { 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
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575 };\r
576 \r
577 MyFont *font[NUM_SIZES][NUM_FONTS];\r
578 \r
579 typedef struct {\r
580   char *label;\r
581   int id;\r
582   HWND hwnd;\r
583   WNDPROC wndproc;\r
584 } MyButtonDesc;\r
585 \r
586 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
587 #define N_BUTTONS 5\r
588 \r
589 MyButtonDesc buttonDesc[N_BUTTONS] =\r
590 {\r
591   {"<<", IDM_ToStart, NULL, NULL},\r
592   {"<", IDM_Backward, NULL, NULL},\r
593   {"P", IDM_Pause, NULL, NULL},\r
594   {">", IDM_Forward, NULL, NULL},\r
595   {">>", IDM_ToEnd, NULL, NULL},\r
596 };\r
597 \r
598 int tinyLayout = 0, smallLayout = 0;\r
599 #define MENU_BAR_ITEMS 9\r
600 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
601   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
602   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
603 };\r
604 \r
605 \r
606 MySound sounds[(int)NSoundClasses];\r
607 MyTextAttribs textAttribs[(int)NColorClasses];\r
608 \r
609 MyColorizeAttribs colorizeAttribs[] = {\r
610   { (COLORREF)0, 0, N_("Shout Text") },\r
611   { (COLORREF)0, 0, N_("SShout/CShout") },\r
612   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
613   { (COLORREF)0, 0, N_("Channel Text") },\r
614   { (COLORREF)0, 0, N_("Kibitz Text") },\r
615   { (COLORREF)0, 0, N_("Tell Text") },\r
616   { (COLORREF)0, 0, N_("Challenge Text") },\r
617   { (COLORREF)0, 0, N_("Request Text") },\r
618   { (COLORREF)0, 0, N_("Seek Text") },\r
619   { (COLORREF)0, 0, N_("Normal Text") },\r
620   { (COLORREF)0, 0, N_("None") }\r
621 };\r
622 \r
623 \r
624 \r
625 static char *commentTitle;\r
626 static char *commentText;\r
627 static int commentIndex;\r
628 static Boolean editComment = FALSE;\r
629 \r
630 \r
631 char errorTitle[MSG_SIZ];\r
632 char errorMessage[2*MSG_SIZ];\r
633 HWND errorDialog = NULL;\r
634 BOOLEAN moveErrorMessageUp = FALSE;\r
635 BOOLEAN consoleEcho = TRUE;\r
636 CHARFORMAT consoleCF;\r
637 COLORREF consoleBackgroundColor;\r
638 \r
639 char *programVersion;\r
640 \r
641 #define CPReal 1\r
642 #define CPComm 2\r
643 #define CPSock 3\r
644 #define CPRcmd 4\r
645 typedef int CPKind;\r
646 \r
647 typedef struct {\r
648   CPKind kind;\r
649   HANDLE hProcess;\r
650   DWORD pid;\r
651   HANDLE hTo;\r
652   HANDLE hFrom;\r
653   SOCKET sock;\r
654   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
655 } ChildProc;\r
656 \r
657 #define INPUT_SOURCE_BUF_SIZE 4096\r
658 \r
659 typedef struct _InputSource {\r
660   CPKind kind;\r
661   HANDLE hFile;\r
662   SOCKET sock;\r
663   int lineByLine;\r
664   HANDLE hThread;\r
665   DWORD id;\r
666   char buf[INPUT_SOURCE_BUF_SIZE];\r
667   char *next;\r
668   DWORD count;\r
669   int error;\r
670   InputCallback func;\r
671   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
672   VOIDSTAR closure;\r
673 } InputSource;\r
674 \r
675 InputSource *consoleInputSource;\r
676 \r
677 DCB dcb;\r
678 \r
679 /* forward */\r
680 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
681 VOID ConsoleCreate();\r
682 LRESULT CALLBACK\r
683   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
684 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
685 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
686 VOID ParseCommSettings(char *arg, DCB *dcb);\r
687 LRESULT CALLBACK\r
688   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
689 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
690 void ParseIcsTextMenu(char *icsTextMenuString);\r
691 VOID PopUpNameDialog(char firstchar);\r
692 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
693 \r
694 /* [AS] */\r
695 int NewGameFRC();\r
696 int GameListOptions();\r
697 \r
698 int dummy; // [HGM] for obsolete args\r
699 \r
700 HWND hwndMain = NULL;        /* root window*/\r
701 HWND hwndConsole = NULL;\r
702 HWND commentDialog = NULL;\r
703 HWND moveHistoryDialog = NULL;\r
704 HWND evalGraphDialog = NULL;\r
705 HWND engineOutputDialog = NULL;\r
706 HWND gameListDialog = NULL;\r
707 HWND editTagsDialog = NULL;\r
708 \r
709 int commentUp = FALSE;\r
710 \r
711 WindowPlacement wpMain;\r
712 WindowPlacement wpConsole;\r
713 WindowPlacement wpComment;\r
714 WindowPlacement wpMoveHistory;\r
715 WindowPlacement wpEvalGraph;\r
716 WindowPlacement wpEngineOutput;\r
717 WindowPlacement wpGameList;\r
718 WindowPlacement wpTags;\r
719 \r
720 VOID EngineOptionsPopup(); // [HGM] settings\r
721 \r
722 VOID GothicPopUp(char *title, VariantClass variant);\r
723 /*\r
724  * Setting "frozen" should disable all user input other than deleting\r
725  * the window.  We do this while engines are initializing themselves.\r
726  */\r
727 static int frozen = 0;\r
728 static int oldMenuItemState[MENU_BAR_ITEMS];\r
729 void FreezeUI()\r
730 {\r
731   HMENU hmenu;\r
732   int i;\r
733 \r
734   if (frozen) return;\r
735   frozen = 1;\r
736   hmenu = GetMenu(hwndMain);\r
737   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
738     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
739   }\r
740   DrawMenuBar(hwndMain);\r
741 }\r
742 \r
743 /* Undo a FreezeUI */\r
744 void ThawUI()\r
745 {\r
746   HMENU hmenu;\r
747   int i;\r
748 \r
749   if (!frozen) return;\r
750   frozen = 0;\r
751   hmenu = GetMenu(hwndMain);\r
752   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
753     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
754   }\r
755   DrawMenuBar(hwndMain);\r
756 }\r
757 \r
758 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
759 \r
760 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
761 #ifdef JAWS\r
762 #include "jaws.c"\r
763 #else\r
764 #define JAWS_INIT\r
765 #define JAWS_ARGS\r
766 #define JAWS_ALT_INTERCEPT\r
767 #define JAWS_KBUP_NAVIGATION\r
768 #define JAWS_KBDOWN_NAVIGATION\r
769 #define JAWS_MENU_ITEMS\r
770 #define JAWS_SILENCE\r
771 #define JAWS_REPLAY\r
772 #define JAWS_ACCEL\r
773 #define JAWS_COPYRIGHT\r
774 #define JAWS_DELETE(X) X\r
775 #define SAYMACHINEMOVE()\r
776 #define SAY(X)\r
777 #endif\r
778 \r
779 /*---------------------------------------------------------------------------*\\r
780  *\r
781  * WinMain\r
782  *\r
783 \*---------------------------------------------------------------------------*/\r
784 \r
785 int APIENTRY\r
786 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
787         LPSTR lpCmdLine, int nCmdShow)\r
788 {\r
789   MSG msg;\r
790   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
791 //  INITCOMMONCONTROLSEX ex;\r
792 \r
793   debugFP = stderr;\r
794 \r
795   LoadLibrary("RICHED32.DLL");\r
796   consoleCF.cbSize = sizeof(CHARFORMAT);\r
797 \r
798   if (!InitApplication(hInstance)) {\r
799     return (FALSE);\r
800   }\r
801   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
802     return (FALSE);\r
803   }\r
804 \r
805   JAWS_INIT\r
806   TranslateMenus(1);\r
807 \r
808 //  InitCommonControlsEx(&ex);\r
809   InitCommonControls();\r
810 \r
811   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
812   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
813   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
814 \r
815   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
816 \r
817   while (GetMessage(&msg, /* message structure */\r
818                     NULL, /* handle of window receiving the message */\r
819                     0,    /* lowest message to examine */\r
820                     0))   /* highest message to examine */\r
821     {\r
822 \r
823       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
824         // [HGM] navigate: switch between all windows with tab\r
825         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
826         int i, currentElement = 0;\r
827 \r
828         // first determine what element of the chain we come from (if any)\r
829         if(appData.icsActive) {\r
830             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
831             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
832         }\r
833         if(engineOutputDialog && EngineOutputIsUp()) {\r
834             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
835             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
836         }\r
837         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
838             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
839         }\r
840         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
841         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
842         if(msg.hwnd == e1)                 currentElement = 2; else\r
843         if(msg.hwnd == e2)                 currentElement = 3; else\r
844         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
845         if(msg.hwnd == mh)                currentElement = 4; else\r
846         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
847         if(msg.hwnd == hText)  currentElement = 5; else\r
848         if(msg.hwnd == hInput) currentElement = 6; else\r
849         for (i = 0; i < N_BUTTONS; i++) {\r
850             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
851         }\r
852 \r
853         // determine where to go to\r
854         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
855           do {\r
856             currentElement = (currentElement + direction) % 7;\r
857             switch(currentElement) {\r
858                 case 0:\r
859                   h = hwndMain; break; // passing this case always makes the loop exit\r
860                 case 1:\r
861                   h = buttonDesc[0].hwnd; break; // could be NULL\r
862                 case 2:\r
863                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
864                   h = e1; break;\r
865                 case 3:\r
866                   if(!EngineOutputIsUp()) continue;\r
867                   h = e2; break;\r
868                 case 4:\r
869                   if(!MoveHistoryIsUp()) continue;\r
870                   h = mh; break;\r
871 //              case 6: // input to eval graph does not seem to get here!\r
872 //                if(!EvalGraphIsUp()) continue;\r
873 //                h = evalGraphDialog; break;\r
874                 case 5:\r
875                   if(!appData.icsActive) continue;\r
876                   SAY("display");\r
877                   h = hText; break;\r
878                 case 6:\r
879                   if(!appData.icsActive) continue;\r
880                   SAY("input");\r
881                   h = hInput; break;\r
882             }\r
883           } while(h == 0);\r
884 \r
885           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
886           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
887           SetFocus(h);\r
888 \r
889           continue; // this message now has been processed\r
890         }\r
891       }\r
892 \r
893       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
894           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
895           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
896           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
897           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
898           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
899           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
900           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
901           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
902           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
903         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
904         for(i=0; i<MAX_CHAT; i++) \r
905             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
906                 done = 1; break;\r
907         }\r
908         if(done) continue; // [HGM] chat: end patch\r
909         TranslateMessage(&msg); /* Translates virtual key codes */\r
910         DispatchMessage(&msg);  /* Dispatches message to window */\r
911       }\r
912     }\r
913 \r
914 \r
915   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
916 }\r
917 \r
918 /*---------------------------------------------------------------------------*\\r
919  *\r
920  * Initialization functions\r
921  *\r
922 \*---------------------------------------------------------------------------*/\r
923 \r
924 void\r
925 SetUserLogo()\r
926 {   // update user logo if necessary\r
927     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
928 \r
929     if(appData.autoLogo) {\r
930           curName = UserName();\r
931           if(strcmp(curName, oldUserName)) {\r
932                 GetCurrentDirectory(MSG_SIZ, dir);\r
933                 SetCurrentDirectory(installDir);\r
934                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
935                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
936                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
937                 if(userLogo == NULL)\r
938                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
939                 SetCurrentDirectory(dir); /* return to prev directory */\r
940           }\r
941     }\r
942 }\r
943 \r
944 BOOL\r
945 InitApplication(HINSTANCE hInstance)\r
946 {\r
947   WNDCLASS wc;\r
948 \r
949   /* Fill in window class structure with parameters that describe the */\r
950   /* main window. */\r
951 \r
952   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
953   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
954   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
955   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
956   wc.hInstance     = hInstance;         /* Owner of this class */\r
957   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
958   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
959   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
960   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
961   wc.lpszClassName = szAppName;                 /* Name to register as */\r
962 \r
963   /* Register the window class and return success/failure code. */\r
964   if (!RegisterClass(&wc)) return FALSE;\r
965 \r
966   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
967   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
968   wc.cbClsExtra    = 0;\r
969   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
970   wc.hInstance     = hInstance;\r
971   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
972   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
973   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
974   wc.lpszMenuName  = NULL;\r
975   wc.lpszClassName = szConsoleName;\r
976 \r
977   if (!RegisterClass(&wc)) return FALSE;\r
978   return TRUE;\r
979 }\r
980 \r
981 \r
982 /* Set by InitInstance, used by EnsureOnScreen */\r
983 int screenHeight, screenWidth;\r
984 \r
985 void\r
986 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
987 {\r
988 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
989   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
990   if (*x > screenWidth - 32) *x = 0;\r
991   if (*y > screenHeight - 32) *y = 0;\r
992   if (*x < minX) *x = minX;\r
993   if (*y < minY) *y = minY;\r
994 }\r
995 \r
996 VOID\r
997 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
998 {\r
999   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1000   GetCurrentDirectory(MSG_SIZ, dir);\r
1001   SetCurrentDirectory(installDir);\r
1002   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1003       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1004 \r
1005       if (cps->programLogo == NULL && appData.debugMode) {\r
1006           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1007       }\r
1008   } else if(appData.autoLogo) {\r
1009       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1010         char *opponent = "";\r
1011         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1012         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1013         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1014         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1015             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1016             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1017         }\r
1018       } else\r
1019       if(appData.directory[n] && appData.directory[n][0]) {\r
1020         SetCurrentDirectory(appData.directory[n]);\r
1021         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1022       }\r
1023   }\r
1024   SetCurrentDirectory(dir); /* return to prev directory */\r
1025 }\r
1026 \r
1027 VOID\r
1028 InitTextures()\r
1029 {\r
1030   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1031   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1032   \r
1033   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1034       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1035       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1036       liteBackTextureMode = appData.liteBackTextureMode;\r
1037 \r
1038       if (liteBackTexture == NULL && appData.debugMode) {\r
1039           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1040       }\r
1041   }\r
1042   \r
1043   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1044       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1045       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1046       darkBackTextureMode = appData.darkBackTextureMode;\r
1047 \r
1048       if (darkBackTexture == NULL && appData.debugMode) {\r
1049           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1050       }\r
1051   }\r
1052 }\r
1053 \r
1054 BOOL\r
1055 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1056 {\r
1057   HWND hwnd; /* Main window handle. */\r
1058   int ibs;\r
1059   WINDOWPLACEMENT wp;\r
1060   char *filepart;\r
1061 \r
1062   hInst = hInstance;    /* Store instance handle in our global variable */\r
1063   programName = szAppName;\r
1064 \r
1065   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1066     *filepart = NULLCHAR;\r
1067     SetCurrentDirectory(installDir);\r
1068   } else {\r
1069     GetCurrentDirectory(MSG_SIZ, installDir);\r
1070   }\r
1071   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1072   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1073   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1074   /* xboard, and older WinBoards, controlled the move sound with the\r
1075      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1076      always turn the option on (so that the backend will call us),\r
1077      then let the user turn the sound off by setting it to silence if\r
1078      desired.  To accommodate old winboard.ini files saved by old\r
1079      versions of WinBoard, we also turn off the sound if the option\r
1080      was initially set to false. [HGM] taken out of InitAppData */\r
1081   if (!appData.ringBellAfterMoves) {\r
1082     sounds[(int)SoundMove].name = strdup("");\r
1083     appData.ringBellAfterMoves = TRUE;\r
1084   }\r
1085   if (appData.debugMode) {\r
1086     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1087     setbuf(debugFP, NULL);\r
1088   }\r
1089 \r
1090   LoadLanguageFile(appData.language);\r
1091 \r
1092   InitBackEnd1();\r
1093 \r
1094 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1095 //  InitEngineUCI( installDir, &second );\r
1096 \r
1097   /* Create a main window for this application instance. */\r
1098   hwnd = CreateWindow(szAppName, szTitle,\r
1099                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1100                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1101                       NULL, NULL, hInstance, NULL);\r
1102   hwndMain = hwnd;\r
1103 \r
1104   /* If window could not be created, return "failure" */\r
1105   if (!hwnd) {\r
1106     return (FALSE);\r
1107   }\r
1108 \r
1109   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1110   LoadLogo(&first, 0, FALSE);\r
1111   LoadLogo(&second, 1, appData.icsActive);\r
1112 \r
1113   SetUserLogo();\r
1114 \r
1115   iconWhite = LoadIcon(hInstance, "icon_white");\r
1116   iconBlack = LoadIcon(hInstance, "icon_black");\r
1117   iconCurrent = iconWhite;\r
1118   InitDrawingColors();\r
1119   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1120   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1121   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1122   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1123     /* Compute window size for each board size, and use the largest\r
1124        size that fits on this screen as the default. */\r
1125     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1126     if (boardSize == (BoardSize)-1 &&\r
1127         winH <= screenHeight\r
1128            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1129         && winW <= screenWidth) {\r
1130       boardSize = (BoardSize)ibs;\r
1131     }\r
1132   }\r
1133 \r
1134   InitDrawingSizes(boardSize, 0);\r
1135   RecentEngineMenu(appData.recentEngineList);\r
1136   InitMenuChecks();\r
1137   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1138 \r
1139   /* [AS] Load textures if specified */\r
1140   InitTextures();\r
1141 \r
1142   mysrandom( (unsigned) time(NULL) );\r
1143 \r
1144   /* [AS] Restore layout */\r
1145   if( wpMoveHistory.visible ) {\r
1146       MoveHistoryPopUp();\r
1147   }\r
1148 \r
1149   if( wpEvalGraph.visible ) {\r
1150       EvalGraphPopUp();\r
1151   }\r
1152 \r
1153   if( wpEngineOutput.visible ) {\r
1154       EngineOutputPopUp();\r
1155   }\r
1156 \r
1157   /* Make the window visible; update its client area; and return "success" */\r
1158   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1159   wp.length = sizeof(WINDOWPLACEMENT);\r
1160   wp.flags = 0;\r
1161   wp.showCmd = nCmdShow;\r
1162   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1163   wp.rcNormalPosition.left = wpMain.x;\r
1164   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1165   wp.rcNormalPosition.top = wpMain.y;\r
1166   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1167   SetWindowPlacement(hwndMain, &wp);\r
1168 \r
1169   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1170 \r
1171   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1172                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1173 \r
1174   if (hwndConsole) {\r
1175 #if AOT_CONSOLE\r
1176     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1177                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1178 #endif\r
1179     ShowWindow(hwndConsole, nCmdShow);\r
1180     SetActiveWindow(hwndConsole);\r
1181   }\r
1182   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1183   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1184 \r
1185   return TRUE;\r
1186 \r
1187 }\r
1188 \r
1189 VOID\r
1190 InitMenuChecks()\r
1191 {\r
1192   HMENU hmenu = GetMenu(hwndMain);\r
1193 \r
1194   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1195                         MF_BYCOMMAND|((appData.icsActive &&\r
1196                                        *appData.icsCommPort != NULLCHAR) ?\r
1197                                       MF_ENABLED : MF_GRAYED));\r
1198   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1199                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1200                                      MF_CHECKED : MF_UNCHECKED));\r
1201 }\r
1202 \r
1203 //---------------------------------------------------------------------------------------------------------\r
1204 \r
1205 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1206 #define XBOARD FALSE\r
1207 \r
1208 #define OPTCHAR "/"\r
1209 #define SEPCHAR "="\r
1210 #define TOPLEVEL 0\r
1211 \r
1212 #include "args.h"\r
1213 \r
1214 // front-end part of option handling\r
1215 \r
1216 VOID\r
1217 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1218 {\r
1219   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1220   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1221   DeleteDC(hdc);\r
1222   lf->lfWidth = 0;\r
1223   lf->lfEscapement = 0;\r
1224   lf->lfOrientation = 0;\r
1225   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1226   lf->lfItalic = mfp->italic;\r
1227   lf->lfUnderline = mfp->underline;\r
1228   lf->lfStrikeOut = mfp->strikeout;\r
1229   lf->lfCharSet = mfp->charset;\r
1230   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1231   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1232   lf->lfQuality = DEFAULT_QUALITY;\r
1233   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1234     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1235 }\r
1236 \r
1237 void\r
1238 CreateFontInMF(MyFont *mf)\r
1239\r
1240   LFfromMFP(&mf->lf, &mf->mfp);\r
1241   if (mf->hf) DeleteObject(mf->hf);\r
1242   mf->hf = CreateFontIndirect(&mf->lf);\r
1243 }\r
1244 \r
1245 // [HGM] This platform-dependent table provides the location for storing the color info\r
1246 void *\r
1247 colorVariable[] = {\r
1248   &whitePieceColor, \r
1249   &blackPieceColor, \r
1250   &lightSquareColor,\r
1251   &darkSquareColor, \r
1252   &highlightSquareColor,\r
1253   &premoveHighlightColor,\r
1254   NULL,\r
1255   &consoleBackgroundColor,\r
1256   &appData.fontForeColorWhite,\r
1257   &appData.fontBackColorWhite,\r
1258   &appData.fontForeColorBlack,\r
1259   &appData.fontBackColorBlack,\r
1260   &appData.evalHistColorWhite,\r
1261   &appData.evalHistColorBlack,\r
1262   &appData.highlightArrowColor,\r
1263 };\r
1264 \r
1265 /* Command line font name parser.  NULL name means do nothing.\r
1266    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1267    For backward compatibility, syntax without the colon is also\r
1268    accepted, but font names with digits in them won't work in that case.\r
1269 */\r
1270 VOID\r
1271 ParseFontName(char *name, MyFontParams *mfp)\r
1272 {\r
1273   char *p, *q;\r
1274   if (name == NULL) return;\r
1275   p = name;\r
1276   q = strchr(p, ':');\r
1277   if (q) {\r
1278     if (q - p >= sizeof(mfp->faceName))\r
1279       ExitArgError(_("Font name too long:"), name, TRUE);\r
1280     memcpy(mfp->faceName, p, q - p);\r
1281     mfp->faceName[q - p] = NULLCHAR;\r
1282     p = q + 1;\r
1283   } else {\r
1284     q = mfp->faceName;\r
1285 \r
1286     while (*p && !isdigit(*p)) {\r
1287       *q++ = *p++;\r
1288       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1289         ExitArgError(_("Font name too long:"), name, TRUE);\r
1290     }\r
1291     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1292     *q = NULLCHAR;\r
1293   }\r
1294   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1295   mfp->pointSize = (float) atof(p);\r
1296   mfp->bold = (strchr(p, 'b') != NULL);\r
1297   mfp->italic = (strchr(p, 'i') != NULL);\r
1298   mfp->underline = (strchr(p, 'u') != NULL);\r
1299   mfp->strikeout = (strchr(p, 's') != NULL);\r
1300   mfp->charset = DEFAULT_CHARSET;\r
1301   q = strchr(p, 'c');\r
1302   if (q)\r
1303     mfp->charset = (BYTE) atoi(q+1);\r
1304 }\r
1305 \r
1306 void\r
1307 ParseFont(char *name, int number)\r
1308 { // wrapper to shield back-end from 'font'\r
1309   ParseFontName(name, &font[boardSize][number]->mfp);\r
1310 }\r
1311 \r
1312 void\r
1313 SetFontDefaults()\r
1314 { // in WB  we have a 2D array of fonts; this initializes their description\r
1315   int i, j;\r
1316   /* Point font array elements to structures and\r
1317      parse default font names */\r
1318   for (i=0; i<NUM_FONTS; i++) {\r
1319     for (j=0; j<NUM_SIZES; j++) {\r
1320       font[j][i] = &fontRec[j][i];\r
1321       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1322     }\r
1323   }\r
1324 }\r
1325 \r
1326 void\r
1327 CreateFonts()\r
1328 { // here we create the actual fonts from the selected descriptions\r
1329   int i, j;\r
1330   for (i=0; i<NUM_FONTS; i++) {\r
1331     for (j=0; j<NUM_SIZES; j++) {\r
1332       CreateFontInMF(font[j][i]);\r
1333     }\r
1334   }\r
1335 }\r
1336 /* Color name parser.\r
1337    X version accepts X color names, but this one\r
1338    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1339 COLORREF\r
1340 ParseColorName(char *name)\r
1341 {\r
1342   int red, green, blue, count;\r
1343   char buf[MSG_SIZ];\r
1344 \r
1345   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1346   if (count != 3) {\r
1347     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1348       &red, &green, &blue);\r
1349   }\r
1350   if (count != 3) {\r
1351     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1352     DisplayError(buf, 0);\r
1353     return RGB(0, 0, 0);\r
1354   }\r
1355   return PALETTERGB(red, green, blue);\r
1356 }\r
1357 \r
1358 void\r
1359 ParseColor(int n, char *name)\r
1360 { // for WinBoard the color is an int, which needs to be derived from the string\r
1361   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1362 }\r
1363 \r
1364 void\r
1365 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1366 {\r
1367   char *e = argValue;\r
1368   int eff = 0;\r
1369 \r
1370   while (*e) {\r
1371     if (*e == 'b')      eff |= CFE_BOLD;\r
1372     else if (*e == 'i') eff |= CFE_ITALIC;\r
1373     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1374     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1375     else if (*e == '#' || isdigit(*e)) break;\r
1376     e++;\r
1377   }\r
1378   *effects = eff;\r
1379   *color   = ParseColorName(e);\r
1380 }\r
1381 \r
1382 void\r
1383 ParseTextAttribs(ColorClass cc, char *s)\r
1384 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1385     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1386     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1387 }\r
1388 \r
1389 void\r
1390 ParseBoardSize(void *addr, char *name)\r
1391 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1392   BoardSize bs = SizeTiny;\r
1393   while (sizeInfo[bs].name != NULL) {\r
1394     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1395         *(BoardSize *)addr = bs;\r
1396         return;\r
1397     }\r
1398     bs++;\r
1399   }\r
1400   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1401 }\r
1402 \r
1403 void\r
1404 LoadAllSounds()\r
1405 { // [HGM] import name from appData first\r
1406   ColorClass cc;\r
1407   SoundClass sc;\r
1408   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1409     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1410     textAttribs[cc].sound.data = NULL;\r
1411     MyLoadSound(&textAttribs[cc].sound);\r
1412   }\r
1413   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1414     textAttribs[cc].sound.name = strdup("");\r
1415     textAttribs[cc].sound.data = NULL;\r
1416   }\r
1417   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1418     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1419     sounds[sc].data = NULL;\r
1420     MyLoadSound(&sounds[sc]);\r
1421   }\r
1422 }\r
1423 \r
1424 void\r
1425 SetCommPortDefaults()\r
1426 {\r
1427    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1428   dcb.DCBlength = sizeof(DCB);\r
1429   dcb.BaudRate = 9600;\r
1430   dcb.fBinary = TRUE;\r
1431   dcb.fParity = FALSE;\r
1432   dcb.fOutxCtsFlow = FALSE;\r
1433   dcb.fOutxDsrFlow = FALSE;\r
1434   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1435   dcb.fDsrSensitivity = FALSE;\r
1436   dcb.fTXContinueOnXoff = TRUE;\r
1437   dcb.fOutX = FALSE;\r
1438   dcb.fInX = FALSE;\r
1439   dcb.fNull = FALSE;\r
1440   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1441   dcb.fAbortOnError = FALSE;\r
1442   dcb.ByteSize = 7;\r
1443   dcb.Parity = SPACEPARITY;\r
1444   dcb.StopBits = ONESTOPBIT;\r
1445 }\r
1446 \r
1447 // [HGM] args: these three cases taken out to stay in front-end\r
1448 void\r
1449 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1450 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1451         // while the curent board size determines the element. This system should be ported to XBoard.\r
1452         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1453         int bs;\r
1454         for (bs=0; bs<NUM_SIZES; bs++) {\r
1455           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1456           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1457           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1458             ad->argName, mfp->faceName, mfp->pointSize,\r
1459             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1460             mfp->bold ? "b" : "",\r
1461             mfp->italic ? "i" : "",\r
1462             mfp->underline ? "u" : "",\r
1463             mfp->strikeout ? "s" : "",\r
1464             (int)mfp->charset);\r
1465         }\r
1466       }\r
1467 \r
1468 void\r
1469 ExportSounds()\r
1470 { // [HGM] copy the names from the internal WB variables to appData\r
1471   ColorClass cc;\r
1472   SoundClass sc;\r
1473   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1474     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1475   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1476     (&appData.soundMove)[sc] = sounds[sc].name;\r
1477 }\r
1478 \r
1479 void\r
1480 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1481 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1482         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1483         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1484           (ta->effects & CFE_BOLD) ? "b" : "",\r
1485           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1486           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1487           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1488           (ta->effects) ? " " : "",\r
1489           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1490       }\r
1491 \r
1492 void\r
1493 SaveColor(FILE *f, ArgDescriptor *ad)\r
1494 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1495         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1496         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1497           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1498 }\r
1499 \r
1500 void\r
1501 SaveBoardSize(FILE *f, char *name, void *addr)\r
1502 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1503   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1504 }\r
1505 \r
1506 void\r
1507 ParseCommPortSettings(char *s)\r
1508 { // wrapper to keep dcb from back-end\r
1509   ParseCommSettings(s, &dcb);\r
1510 }\r
1511 \r
1512 void\r
1513 GetWindowCoords()\r
1514 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1515   GetActualPlacement(hwndMain, &wpMain);\r
1516   GetActualPlacement(hwndConsole, &wpConsole);\r
1517   GetActualPlacement(commentDialog, &wpComment);\r
1518   GetActualPlacement(editTagsDialog, &wpTags);\r
1519   GetActualPlacement(gameListDialog, &wpGameList);\r
1520   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1521   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1522   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1523 }\r
1524 \r
1525 void\r
1526 PrintCommPortSettings(FILE *f, char *name)\r
1527 { // wrapper to shield back-end from DCB\r
1528       PrintCommSettings(f, name, &dcb);\r
1529 }\r
1530 \r
1531 int\r
1532 MySearchPath(char *installDir, char *name, char *fullname)\r
1533 {\r
1534   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1535   if(name[0]== '%') {\r
1536     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1537     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1538       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1539       *strchr(buf, '%') = 0;\r
1540       strcat(fullname, getenv(buf));\r
1541       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1542     }\r
1543     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1544     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1545     return (int) strlen(fullname);\r
1546   }\r
1547   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1548 }\r
1549 \r
1550 int\r
1551 MyGetFullPathName(char *name, char *fullname)\r
1552 {\r
1553   char *dummy;\r
1554   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1555 }\r
1556 \r
1557 int\r
1558 MainWindowUp()\r
1559 { // [HGM] args: allows testing if main window is realized from back-end\r
1560   return hwndMain != NULL;\r
1561 }\r
1562 \r
1563 void\r
1564 PopUpStartupDialog()\r
1565 {\r
1566     FARPROC lpProc;\r
1567     \r
1568     LoadLanguageFile(appData.language);\r
1569     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1570     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1571     FreeProcInstance(lpProc);\r
1572 }\r
1573 \r
1574 /*---------------------------------------------------------------------------*\\r
1575  *\r
1576  * GDI board drawing routines\r
1577  *\r
1578 \*---------------------------------------------------------------------------*/\r
1579 \r
1580 /* [AS] Draw square using background texture */\r
1581 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1582 {\r
1583     XFORM   x;\r
1584 \r
1585     if( mode == 0 ) {\r
1586         return; /* Should never happen! */\r
1587     }\r
1588 \r
1589     SetGraphicsMode( dst, GM_ADVANCED );\r
1590 \r
1591     switch( mode ) {\r
1592     case 1:\r
1593         /* Identity */\r
1594         break;\r
1595     case 2:\r
1596         /* X reflection */\r
1597         x.eM11 = -1.0;\r
1598         x.eM12 = 0;\r
1599         x.eM21 = 0;\r
1600         x.eM22 = 1.0;\r
1601         x.eDx = (FLOAT) dw + dx - 1;\r
1602         x.eDy = 0;\r
1603         dx = 0;\r
1604         SetWorldTransform( dst, &x );\r
1605         break;\r
1606     case 3:\r
1607         /* Y reflection */\r
1608         x.eM11 = 1.0;\r
1609         x.eM12 = 0;\r
1610         x.eM21 = 0;\r
1611         x.eM22 = -1.0;\r
1612         x.eDx = 0;\r
1613         x.eDy = (FLOAT) dh + dy - 1;\r
1614         dy = 0;\r
1615         SetWorldTransform( dst, &x );\r
1616         break;\r
1617     case 4:\r
1618         /* X/Y flip */\r
1619         x.eM11 = 0;\r
1620         x.eM12 = 1.0;\r
1621         x.eM21 = 1.0;\r
1622         x.eM22 = 0;\r
1623         x.eDx = (FLOAT) dx;\r
1624         x.eDy = (FLOAT) dy;\r
1625         dx = 0;\r
1626         dy = 0;\r
1627         SetWorldTransform( dst, &x );\r
1628         break;\r
1629     }\r
1630 \r
1631     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1632 \r
1633     x.eM11 = 1.0;\r
1634     x.eM12 = 0;\r
1635     x.eM21 = 0;\r
1636     x.eM22 = 1.0;\r
1637     x.eDx = 0;\r
1638     x.eDy = 0;\r
1639     SetWorldTransform( dst, &x );\r
1640 \r
1641     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1642 }\r
1643 \r
1644 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1645 enum {\r
1646     PM_WP = (int) WhitePawn, \r
1647     PM_WN = (int) WhiteKnight, \r
1648     PM_WB = (int) WhiteBishop, \r
1649     PM_WR = (int) WhiteRook, \r
1650     PM_WQ = (int) WhiteQueen, \r
1651     PM_WF = (int) WhiteFerz, \r
1652     PM_WW = (int) WhiteWazir, \r
1653     PM_WE = (int) WhiteAlfil, \r
1654     PM_WM = (int) WhiteMan, \r
1655     PM_WO = (int) WhiteCannon, \r
1656     PM_WU = (int) WhiteUnicorn, \r
1657     PM_WH = (int) WhiteNightrider, \r
1658     PM_WA = (int) WhiteAngel, \r
1659     PM_WC = (int) WhiteMarshall, \r
1660     PM_WAB = (int) WhiteCardinal, \r
1661     PM_WD = (int) WhiteDragon, \r
1662     PM_WL = (int) WhiteLance, \r
1663     PM_WS = (int) WhiteCobra, \r
1664     PM_WV = (int) WhiteFalcon, \r
1665     PM_WSG = (int) WhiteSilver, \r
1666     PM_WG = (int) WhiteGrasshopper, \r
1667     PM_WK = (int) WhiteKing,\r
1668     PM_BP = (int) BlackPawn, \r
1669     PM_BN = (int) BlackKnight, \r
1670     PM_BB = (int) BlackBishop, \r
1671     PM_BR = (int) BlackRook, \r
1672     PM_BQ = (int) BlackQueen, \r
1673     PM_BF = (int) BlackFerz, \r
1674     PM_BW = (int) BlackWazir, \r
1675     PM_BE = (int) BlackAlfil, \r
1676     PM_BM = (int) BlackMan,\r
1677     PM_BO = (int) BlackCannon, \r
1678     PM_BU = (int) BlackUnicorn, \r
1679     PM_BH = (int) BlackNightrider, \r
1680     PM_BA = (int) BlackAngel, \r
1681     PM_BC = (int) BlackMarshall, \r
1682     PM_BG = (int) BlackGrasshopper, \r
1683     PM_BAB = (int) BlackCardinal,\r
1684     PM_BD = (int) BlackDragon,\r
1685     PM_BL = (int) BlackLance,\r
1686     PM_BS = (int) BlackCobra,\r
1687     PM_BV = (int) BlackFalcon,\r
1688     PM_BSG = (int) BlackSilver,\r
1689     PM_BK = (int) BlackKing\r
1690 };\r
1691 \r
1692 static HFONT hPieceFont = NULL;\r
1693 static HBITMAP hPieceMask[(int) EmptySquare];\r
1694 static HBITMAP hPieceFace[(int) EmptySquare];\r
1695 static int fontBitmapSquareSize = 0;\r
1696 static char pieceToFontChar[(int) EmptySquare] =\r
1697                               { 'p', 'n', 'b', 'r', 'q', \r
1698                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1699                       'k', 'o', 'm', 'v', 't', 'w', \r
1700                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1701                                                               'l' };\r
1702 \r
1703 extern BOOL SetCharTable( char *table, const char * map );\r
1704 /* [HGM] moved to backend.c */\r
1705 \r
1706 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1707 {\r
1708     HBRUSH hbrush;\r
1709     BYTE r1 = GetRValue( color );\r
1710     BYTE g1 = GetGValue( color );\r
1711     BYTE b1 = GetBValue( color );\r
1712     BYTE r2 = r1 / 2;\r
1713     BYTE g2 = g1 / 2;\r
1714     BYTE b2 = b1 / 2;\r
1715     RECT rc;\r
1716 \r
1717     /* Create a uniform background first */\r
1718     hbrush = CreateSolidBrush( color );\r
1719     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1720     FillRect( hdc, &rc, hbrush );\r
1721     DeleteObject( hbrush );\r
1722     \r
1723     if( mode == 1 ) {\r
1724         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1725         int steps = squareSize / 2;\r
1726         int i;\r
1727 \r
1728         for( i=0; i<steps; i++ ) {\r
1729             BYTE r = r1 - (r1-r2) * i / steps;\r
1730             BYTE g = g1 - (g1-g2) * i / steps;\r
1731             BYTE b = b1 - (b1-b2) * i / steps;\r
1732 \r
1733             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1734             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1735             FillRect( hdc, &rc, hbrush );\r
1736             DeleteObject(hbrush);\r
1737         }\r
1738     }\r
1739     else if( mode == 2 ) {\r
1740         /* Diagonal gradient, good more or less for every piece */\r
1741         POINT triangle[3];\r
1742         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1743         HBRUSH hbrush_old;\r
1744         int steps = squareSize;\r
1745         int i;\r
1746 \r
1747         triangle[0].x = squareSize - steps;\r
1748         triangle[0].y = squareSize;\r
1749         triangle[1].x = squareSize;\r
1750         triangle[1].y = squareSize;\r
1751         triangle[2].x = squareSize;\r
1752         triangle[2].y = squareSize - steps;\r
1753 \r
1754         for( i=0; i<steps; i++ ) {\r
1755             BYTE r = r1 - (r1-r2) * i / steps;\r
1756             BYTE g = g1 - (g1-g2) * i / steps;\r
1757             BYTE b = b1 - (b1-b2) * i / steps;\r
1758 \r
1759             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1760             hbrush_old = SelectObject( hdc, hbrush );\r
1761             Polygon( hdc, triangle, 3 );\r
1762             SelectObject( hdc, hbrush_old );\r
1763             DeleteObject(hbrush);\r
1764             triangle[0].x++;\r
1765             triangle[2].y++;\r
1766         }\r
1767 \r
1768         SelectObject( hdc, hpen );\r
1769     }\r
1770 }\r
1771 \r
1772 /*\r
1773     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1774     seems to work ok. The main problem here is to find the "inside" of a chess\r
1775     piece: follow the steps as explained below.\r
1776 */\r
1777 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1778 {\r
1779     HBITMAP hbm;\r
1780     HBITMAP hbm_old;\r
1781     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1782     RECT rc;\r
1783     SIZE sz;\r
1784     POINT pt;\r
1785     int backColor = whitePieceColor; \r
1786     int foreColor = blackPieceColor;\r
1787     \r
1788     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1789         backColor = appData.fontBackColorWhite;\r
1790         foreColor = appData.fontForeColorWhite;\r
1791     }\r
1792     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1793         backColor = appData.fontBackColorBlack;\r
1794         foreColor = appData.fontForeColorBlack;\r
1795     }\r
1796 \r
1797     /* Mask */\r
1798     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1799 \r
1800     hbm_old = SelectObject( hdc, hbm );\r
1801 \r
1802     rc.left = 0;\r
1803     rc.top = 0;\r
1804     rc.right = squareSize;\r
1805     rc.bottom = squareSize;\r
1806 \r
1807     /* Step 1: background is now black */\r
1808     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1809 \r
1810     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1811 \r
1812     pt.x = (squareSize - sz.cx) / 2;\r
1813     pt.y = (squareSize - sz.cy) / 2;\r
1814 \r
1815     SetBkMode( hdc, TRANSPARENT );\r
1816     SetTextColor( hdc, chroma );\r
1817     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1818     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1819 \r
1820     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1821     /* Step 3: the area outside the piece is filled with white */\r
1822 //    FloodFill( hdc, 0, 0, chroma );\r
1823     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1824     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1825     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1826     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1827     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1828     /* \r
1829         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1830         but if the start point is not inside the piece we're lost!\r
1831         There should be a better way to do this... if we could create a region or path\r
1832         from the fill operation we would be fine for example.\r
1833     */\r
1834 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1835     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1836 \r
1837     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1838         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1839         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1840 \r
1841         SelectObject( dc2, bm2 );\r
1842         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1843         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1844         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1845         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1846         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1847 \r
1848         DeleteDC( dc2 );\r
1849         DeleteObject( bm2 );\r
1850     }\r
1851 \r
1852     SetTextColor( hdc, 0 );\r
1853     /* \r
1854         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1855         draw the piece again in black for safety.\r
1856     */\r
1857     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1858 \r
1859     SelectObject( hdc, hbm_old );\r
1860 \r
1861     if( hPieceMask[index] != NULL ) {\r
1862         DeleteObject( hPieceMask[index] );\r
1863     }\r
1864 \r
1865     hPieceMask[index] = hbm;\r
1866 \r
1867     /* Face */\r
1868     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1869 \r
1870     SelectObject( hdc, hbm );\r
1871 \r
1872     {\r
1873         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1874         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1875         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1876 \r
1877         SelectObject( dc1, hPieceMask[index] );\r
1878         SelectObject( dc2, bm2 );\r
1879         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1880         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1881         \r
1882         /* \r
1883             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1884             the piece background and deletes (makes transparent) the rest.\r
1885             Thanks to that mask, we are free to paint the background with the greates\r
1886             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1887             We use this, to make gradients and give the pieces a "roundish" look.\r
1888         */\r
1889         SetPieceBackground( hdc, backColor, 2 );\r
1890         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1891 \r
1892         DeleteDC( dc2 );\r
1893         DeleteDC( dc1 );\r
1894         DeleteObject( bm2 );\r
1895     }\r
1896 \r
1897     SetTextColor( hdc, foreColor );\r
1898     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1899 \r
1900     SelectObject( hdc, hbm_old );\r
1901 \r
1902     if( hPieceFace[index] != NULL ) {\r
1903         DeleteObject( hPieceFace[index] );\r
1904     }\r
1905 \r
1906     hPieceFace[index] = hbm;\r
1907 }\r
1908 \r
1909 static int TranslatePieceToFontPiece( int piece )\r
1910 {\r
1911     switch( piece ) {\r
1912     case BlackPawn:\r
1913         return PM_BP;\r
1914     case BlackKnight:\r
1915         return PM_BN;\r
1916     case BlackBishop:\r
1917         return PM_BB;\r
1918     case BlackRook:\r
1919         return PM_BR;\r
1920     case BlackQueen:\r
1921         return PM_BQ;\r
1922     case BlackKing:\r
1923         return PM_BK;\r
1924     case WhitePawn:\r
1925         return PM_WP;\r
1926     case WhiteKnight:\r
1927         return PM_WN;\r
1928     case WhiteBishop:\r
1929         return PM_WB;\r
1930     case WhiteRook:\r
1931         return PM_WR;\r
1932     case WhiteQueen:\r
1933         return PM_WQ;\r
1934     case WhiteKing:\r
1935         return PM_WK;\r
1936 \r
1937     case BlackAngel:\r
1938         return PM_BA;\r
1939     case BlackMarshall:\r
1940         return PM_BC;\r
1941     case BlackFerz:\r
1942         return PM_BF;\r
1943     case BlackNightrider:\r
1944         return PM_BH;\r
1945     case BlackAlfil:\r
1946         return PM_BE;\r
1947     case BlackWazir:\r
1948         return PM_BW;\r
1949     case BlackUnicorn:\r
1950         return PM_BU;\r
1951     case BlackCannon:\r
1952         return PM_BO;\r
1953     case BlackGrasshopper:\r
1954         return PM_BG;\r
1955     case BlackMan:\r
1956         return PM_BM;\r
1957     case BlackSilver:\r
1958         return PM_BSG;\r
1959     case BlackLance:\r
1960         return PM_BL;\r
1961     case BlackFalcon:\r
1962         return PM_BV;\r
1963     case BlackCobra:\r
1964         return PM_BS;\r
1965     case BlackCardinal:\r
1966         return PM_BAB;\r
1967     case BlackDragon:\r
1968         return PM_BD;\r
1969 \r
1970     case WhiteAngel:\r
1971         return PM_WA;\r
1972     case WhiteMarshall:\r
1973         return PM_WC;\r
1974     case WhiteFerz:\r
1975         return PM_WF;\r
1976     case WhiteNightrider:\r
1977         return PM_WH;\r
1978     case WhiteAlfil:\r
1979         return PM_WE;\r
1980     case WhiteWazir:\r
1981         return PM_WW;\r
1982     case WhiteUnicorn:\r
1983         return PM_WU;\r
1984     case WhiteCannon:\r
1985         return PM_WO;\r
1986     case WhiteGrasshopper:\r
1987         return PM_WG;\r
1988     case WhiteMan:\r
1989         return PM_WM;\r
1990     case WhiteSilver:\r
1991         return PM_WSG;\r
1992     case WhiteLance:\r
1993         return PM_WL;\r
1994     case WhiteFalcon:\r
1995         return PM_WV;\r
1996     case WhiteCobra:\r
1997         return PM_WS;\r
1998     case WhiteCardinal:\r
1999         return PM_WAB;\r
2000     case WhiteDragon:\r
2001         return PM_WD;\r
2002     }\r
2003 \r
2004     return 0;\r
2005 }\r
2006 \r
2007 void CreatePiecesFromFont()\r
2008 {\r
2009     LOGFONT lf;\r
2010     HDC hdc_window = NULL;\r
2011     HDC hdc = NULL;\r
2012     HFONT hfont_old;\r
2013     int fontHeight;\r
2014     int i;\r
2015 \r
2016     if( fontBitmapSquareSize < 0 ) {\r
2017         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2018         return;\r
2019     }\r
2020 \r
2021     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2022             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2023         fontBitmapSquareSize = -1;\r
2024         return;\r
2025     }\r
2026 \r
2027     if( fontBitmapSquareSize != squareSize ) {\r
2028         hdc_window = GetDC( hwndMain );\r
2029         hdc = CreateCompatibleDC( hdc_window );\r
2030 \r
2031         if( hPieceFont != NULL ) {\r
2032             DeleteObject( hPieceFont );\r
2033         }\r
2034         else {\r
2035             for( i=0; i<=(int)BlackKing; i++ ) {\r
2036                 hPieceMask[i] = NULL;\r
2037                 hPieceFace[i] = NULL;\r
2038             }\r
2039         }\r
2040 \r
2041         fontHeight = 75;\r
2042 \r
2043         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2044             fontHeight = appData.fontPieceSize;\r
2045         }\r
2046 \r
2047         fontHeight = (fontHeight * squareSize) / 100;\r
2048 \r
2049         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2050         lf.lfWidth = 0;\r
2051         lf.lfEscapement = 0;\r
2052         lf.lfOrientation = 0;\r
2053         lf.lfWeight = FW_NORMAL;\r
2054         lf.lfItalic = 0;\r
2055         lf.lfUnderline = 0;\r
2056         lf.lfStrikeOut = 0;\r
2057         lf.lfCharSet = DEFAULT_CHARSET;\r
2058         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2059         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2060         lf.lfQuality = PROOF_QUALITY;\r
2061         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2062         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2063         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2064 \r
2065         hPieceFont = CreateFontIndirect( &lf );\r
2066 \r
2067         if( hPieceFont == NULL ) {\r
2068             fontBitmapSquareSize = -2;\r
2069         }\r
2070         else {\r
2071             /* Setup font-to-piece character table */\r
2072             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2073                 /* No (or wrong) global settings, try to detect the font */\r
2074                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2075                     /* Alpha */\r
2076                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2077                 }\r
2078                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2079                     /* DiagramTT* family */\r
2080                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2081                 }\r
2082                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2083                     /* Fairy symbols */\r
2084                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2085                 }\r
2086                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2087                     /* Good Companion (Some characters get warped as literal :-( */\r
2088                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2089                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2090                     SetCharTable(pieceToFontChar, s);\r
2091                 }\r
2092                 else {\r
2093                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2094                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2095                 }\r
2096             }\r
2097 \r
2098             /* Create bitmaps */\r
2099             hfont_old = SelectObject( hdc, hPieceFont );\r
2100             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2101                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2102                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2103 \r
2104             SelectObject( hdc, hfont_old );\r
2105 \r
2106             fontBitmapSquareSize = squareSize;\r
2107         }\r
2108     }\r
2109 \r
2110     if( hdc != NULL ) {\r
2111         DeleteDC( hdc );\r
2112     }\r
2113 \r
2114     if( hdc_window != NULL ) {\r
2115         ReleaseDC( hwndMain, hdc_window );\r
2116     }\r
2117 }\r
2118 \r
2119 HBITMAP\r
2120 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2121 {\r
2122   char name[128], buf[MSG_SIZ];\r
2123 \r
2124     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2125   if(appData.pieceDirectory[0]) {\r
2126     HBITMAP res;\r
2127     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2128     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2129     if(res) return res;\r
2130   }\r
2131   if (gameInfo.event &&\r
2132       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2133       strcmp(name, "k80s") == 0) {\r
2134     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2135   }\r
2136   return LoadBitmap(hinst, name);\r
2137 }\r
2138 \r
2139 \r
2140 /* Insert a color into the program's logical palette\r
2141    structure.  This code assumes the given color is\r
2142    the result of the RGB or PALETTERGB macro, and it\r
2143    knows how those macros work (which is documented).\r
2144 */\r
2145 VOID\r
2146 InsertInPalette(COLORREF color)\r
2147 {\r
2148   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2149 \r
2150   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2151     DisplayFatalError(_("Too many colors"), 0, 1);\r
2152     pLogPal->palNumEntries--;\r
2153     return;\r
2154   }\r
2155 \r
2156   pe->peFlags = (char) 0;\r
2157   pe->peRed = (char) (0xFF & color);\r
2158   pe->peGreen = (char) (0xFF & (color >> 8));\r
2159   pe->peBlue = (char) (0xFF & (color >> 16));\r
2160   return;\r
2161 }\r
2162 \r
2163 \r
2164 VOID\r
2165 InitDrawingColors()\r
2166 {\r
2167   if (pLogPal == NULL) {\r
2168     /* Allocate enough memory for a logical palette with\r
2169      * PALETTESIZE entries and set the size and version fields\r
2170      * of the logical palette structure.\r
2171      */\r
2172     pLogPal = (NPLOGPALETTE)\r
2173       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2174                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2175     pLogPal->palVersion    = 0x300;\r
2176   }\r
2177   pLogPal->palNumEntries = 0;\r
2178 \r
2179   InsertInPalette(lightSquareColor);\r
2180   InsertInPalette(darkSquareColor);\r
2181   InsertInPalette(whitePieceColor);\r
2182   InsertInPalette(blackPieceColor);\r
2183   InsertInPalette(highlightSquareColor);\r
2184   InsertInPalette(premoveHighlightColor);\r
2185 \r
2186   /*  create a logical color palette according the information\r
2187    *  in the LOGPALETTE structure.\r
2188    */\r
2189   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2190 \r
2191   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2192   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2193   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2194   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2195   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2196   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2197   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2198   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2199   /* [AS] Force rendering of the font-based pieces */\r
2200   if( fontBitmapSquareSize > 0 ) {\r
2201     fontBitmapSquareSize = 0;\r
2202   }\r
2203 }\r
2204 \r
2205 \r
2206 int\r
2207 BoardWidth(int boardSize, int n)\r
2208 { /* [HGM] argument n added to allow different width and height */\r
2209   int lineGap = sizeInfo[boardSize].lineGap;\r
2210 \r
2211   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2212       lineGap = appData.overrideLineGap;\r
2213   }\r
2214 \r
2215   return (n + 1) * lineGap +\r
2216           n * sizeInfo[boardSize].squareSize;\r
2217 }\r
2218 \r
2219 /* Respond to board resize by dragging edge */\r
2220 VOID\r
2221 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2222 {\r
2223   BoardSize newSize = NUM_SIZES - 1;\r
2224   static int recurse = 0;\r
2225   if (IsIconic(hwndMain)) return;\r
2226   if (recurse > 0) return;\r
2227   recurse++;\r
2228   while (newSize > 0) {\r
2229         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2230         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2231            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2232     newSize--;\r
2233   } \r
2234   boardSize = newSize;\r
2235   InitDrawingSizes(boardSize, flags);\r
2236   recurse--;\r
2237 }\r
2238 \r
2239 \r
2240 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2241 \r
2242 VOID\r
2243 InitDrawingSizes(BoardSize boardSize, int flags)\r
2244 {\r
2245   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2246   ChessSquare piece;\r
2247   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2248   HDC hdc;\r
2249   SIZE clockSize, messageSize;\r
2250   HFONT oldFont;\r
2251   char buf[MSG_SIZ];\r
2252   char *str;\r
2253   HMENU hmenu = GetMenu(hwndMain);\r
2254   RECT crect, wrect, oldRect;\r
2255   int offby;\r
2256   LOGBRUSH logbrush;\r
2257   VariantClass v = gameInfo.variant;\r
2258 \r
2259   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2260   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2261 \r
2262   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2263   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2264   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2265   oldBoardSize = boardSize;\r
2266 \r
2267   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2268   { // correct board size to one where built-in pieces exist\r
2269     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2270        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2271       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2272       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2273       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2274       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2275       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2276                                    boardSize = SizeMiddling;\r
2277     }\r
2278   }\r
2279   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2280 \r
2281   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2282   oldRect.top = wpMain.y;\r
2283   oldRect.right = wpMain.x + wpMain.width;\r
2284   oldRect.bottom = wpMain.y + wpMain.height;\r
2285 \r
2286   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2287   smallLayout = sizeInfo[boardSize].smallLayout;\r
2288   squareSize = sizeInfo[boardSize].squareSize;\r
2289   lineGap = sizeInfo[boardSize].lineGap;\r
2290   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2291   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2292 \r
2293   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2294       lineGap = appData.overrideLineGap;\r
2295   }\r
2296 \r
2297   if (tinyLayout != oldTinyLayout) {\r
2298     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2299     if (tinyLayout) {\r
2300       style &= ~WS_SYSMENU;\r
2301       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2302                  "&Minimize\tCtrl+F4");\r
2303     } else {\r
2304       style |= WS_SYSMENU;\r
2305       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2306     }\r
2307     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2308 \r
2309     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2310       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2311         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2312     }\r
2313     DrawMenuBar(hwndMain);\r
2314   }\r
2315 \r
2316   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2317   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2318 \r
2319   /* Get text area sizes */\r
2320   hdc = GetDC(hwndMain);\r
2321   if (appData.clockMode) {\r
2322     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2323   } else {\r
2324     snprintf(buf, MSG_SIZ, _("White"));\r
2325   }\r
2326   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2327   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2328   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2329   str = _("We only care about the height here");\r
2330   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2331   SelectObject(hdc, oldFont);\r
2332   ReleaseDC(hwndMain, hdc);\r
2333 \r
2334   /* Compute where everything goes */\r
2335   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2336         /* [HGM] logo: if either logo is on, reserve space for it */\r
2337         logoHeight =  2*clockSize.cy;\r
2338         leftLogoRect.left   = OUTER_MARGIN;\r
2339         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2340         leftLogoRect.top    = OUTER_MARGIN;\r
2341         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2342 \r
2343         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2344         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2345         rightLogoRect.top    = OUTER_MARGIN;\r
2346         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2347 \r
2348 \r
2349     whiteRect.left = leftLogoRect.right;\r
2350     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2351     whiteRect.top = OUTER_MARGIN;\r
2352     whiteRect.bottom = whiteRect.top + logoHeight;\r
2353 \r
2354     blackRect.right = rightLogoRect.left;\r
2355     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2356     blackRect.top = whiteRect.top;\r
2357     blackRect.bottom = whiteRect.bottom;\r
2358   } else {\r
2359     whiteRect.left = OUTER_MARGIN;\r
2360     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2361     whiteRect.top = OUTER_MARGIN;\r
2362     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2363 \r
2364     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2365     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2366     blackRect.top = whiteRect.top;\r
2367     blackRect.bottom = whiteRect.bottom;\r
2368 \r
2369     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2370   }\r
2371 \r
2372   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2373   if (appData.showButtonBar) {\r
2374     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2375       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2376   } else {\r
2377     messageRect.right = OUTER_MARGIN + boardWidth;\r
2378   }\r
2379   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2380   messageRect.bottom = messageRect.top + messageSize.cy;\r
2381 \r
2382   boardRect.left = OUTER_MARGIN;\r
2383   boardRect.right = boardRect.left + boardWidth;\r
2384   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2385   boardRect.bottom = boardRect.top + boardHeight;\r
2386 \r
2387   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2388   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2389   oldTinyLayout = tinyLayout;\r
2390   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2391   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2392     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2393   winW *= 1 + twoBoards;\r
2394   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2395   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2396   wpMain.height = winH; //       without disturbing window attachments\r
2397   GetWindowRect(hwndMain, &wrect);\r
2398   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2399                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2400 \r
2401   // [HGM] placement: let attached windows follow size change.\r
2402   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2403   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2404   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2405   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2406   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2407 \r
2408   /* compensate if menu bar wrapped */\r
2409   GetClientRect(hwndMain, &crect);\r
2410   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2411   wpMain.height += offby;\r
2412   switch (flags) {\r
2413   case WMSZ_TOPLEFT:\r
2414     SetWindowPos(hwndMain, NULL, \r
2415                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2416                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2417     break;\r
2418 \r
2419   case WMSZ_TOPRIGHT:\r
2420   case WMSZ_TOP:\r
2421     SetWindowPos(hwndMain, NULL, \r
2422                  wrect.left, wrect.bottom - wpMain.height, \r
2423                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2424     break;\r
2425 \r
2426   case WMSZ_BOTTOMLEFT:\r
2427   case WMSZ_LEFT:\r
2428     SetWindowPos(hwndMain, NULL, \r
2429                  wrect.right - wpMain.width, wrect.top, \r
2430                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2431     break;\r
2432 \r
2433   case WMSZ_BOTTOMRIGHT:\r
2434   case WMSZ_BOTTOM:\r
2435   case WMSZ_RIGHT:\r
2436   default:\r
2437     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2438                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2439     break;\r
2440   }\r
2441 \r
2442   hwndPause = NULL;\r
2443   for (i = 0; i < N_BUTTONS; i++) {\r
2444     if (buttonDesc[i].hwnd != NULL) {\r
2445       DestroyWindow(buttonDesc[i].hwnd);\r
2446       buttonDesc[i].hwnd = NULL;\r
2447     }\r
2448     if (appData.showButtonBar) {\r
2449       buttonDesc[i].hwnd =\r
2450         CreateWindow("BUTTON", buttonDesc[i].label,\r
2451                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2452                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2453                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2454                      (HMENU) buttonDesc[i].id,\r
2455                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2456       if (tinyLayout) {\r
2457         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2458                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2459                     MAKELPARAM(FALSE, 0));\r
2460       }\r
2461       if (buttonDesc[i].id == IDM_Pause)\r
2462         hwndPause = buttonDesc[i].hwnd;\r
2463       buttonDesc[i].wndproc = (WNDPROC)\r
2464         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2465     }\r
2466   }\r
2467   if (gridPen != NULL) DeleteObject(gridPen);\r
2468   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2469   if (premovePen != NULL) DeleteObject(premovePen);\r
2470   if (lineGap != 0) {\r
2471     logbrush.lbStyle = BS_SOLID;\r
2472     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2473     gridPen =\r
2474       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2475                    lineGap, &logbrush, 0, NULL);\r
2476     logbrush.lbColor = highlightSquareColor;\r
2477     highlightPen =\r
2478       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2479                    lineGap, &logbrush, 0, NULL);\r
2480 \r
2481     logbrush.lbColor = premoveHighlightColor; \r
2482     premovePen =\r
2483       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2484                    lineGap, &logbrush, 0, NULL);\r
2485 \r
2486     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2487     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2488       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2489       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2490         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2491       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2492         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2493       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2494     }\r
2495     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2496       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2497       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2498         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2499         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2500       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2501         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2502       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2503     }\r
2504   }\r
2505 \r
2506   /* [HGM] Licensing requirement */\r
2507 #ifdef GOTHIC\r
2508   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2509 #endif\r
2510 #ifdef FALCON\r
2511   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2512 #endif\r
2513   GothicPopUp( "", VariantNormal);\r
2514 \r
2515 \r
2516 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2517 \r
2518   /* Load piece bitmaps for this board size */\r
2519   for (i=0; i<=2; i++) {\r
2520     for (piece = WhitePawn;\r
2521          (int) piece < (int) BlackPawn;\r
2522          piece = (ChessSquare) ((int) piece + 1)) {\r
2523       if (pieceBitmap[i][piece] != NULL)\r
2524         DeleteObject(pieceBitmap[i][piece]);\r
2525     }\r
2526   }\r
2527 \r
2528   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2529   // Orthodox Chess pieces\r
2530   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2531   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2532   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2533   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2534   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2535   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2536   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2537   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2538   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2539   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2540   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2541   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2542   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2543   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2544   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2545   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2546     // in Shogi, Hijack the unused Queen for Lance\r
2547     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2548     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2549     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2550   } else {\r
2551     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2552     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2553     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2554   }\r
2555 \r
2556   if(squareSize <= 72 && squareSize >= 33) { \r
2557     /* A & C are available in most sizes now */\r
2558     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2559       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2560       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2561       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2562       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2563       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2564       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2565       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2566       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2567       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2568       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2569       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2570       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2571     } else { // Smirf-like\r
2572       if(gameInfo.variant == VariantSChess) {\r
2573         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2574         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2575         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2576       } else {\r
2577         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2578         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2579         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2580       }\r
2581     }\r
2582     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2583       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2584       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2585       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2586     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2587       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2588       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2589       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2590     } else { // WinBoard standard\r
2591       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2592       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2593       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2594     }\r
2595   }\r
2596 \r
2597 \r
2598   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2599     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2600     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2601     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2602     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2603     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2604     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2605     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2606     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2607     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2608     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2609     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2610     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2611     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2612     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2613     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2614     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2615     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2616     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2617     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2618     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2619     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2620     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2621     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2622     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2623     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2624     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2625     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2626     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2627     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2628     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2629 \r
2630     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2631       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2632       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2633       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2634       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2635       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2636       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2637       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2638       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2639       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2640       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2641       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2642       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2643     } else {\r
2644       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2645       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2646       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2647       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2648       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2649       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2650       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2651       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2652       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2653       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2654       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2655       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2656     }\r
2657 \r
2658   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2659     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2660     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2661     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2662     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2663     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2664     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2665     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2666     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2667     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2668     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2669     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2670     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2671     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2672     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2673   }\r
2674 \r
2675 \r
2676   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2677   /* special Shogi support in this size */\r
2678   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2679       for (piece = WhitePawn;\r
2680            (int) piece < (int) BlackPawn;\r
2681            piece = (ChessSquare) ((int) piece + 1)) {\r
2682         if (pieceBitmap[i][piece] != NULL)\r
2683           DeleteObject(pieceBitmap[i][piece]);\r
2684       }\r
2685     }\r
2686   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2687   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2688   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2689   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2690   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2691   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2692   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2693   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2694   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2695   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2696   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2697   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2698   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2699   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2700   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2701   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2702   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2703   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2704   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2705   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2706   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2707   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2708   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2709   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2710   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2711   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2712   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2713   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2714   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2715   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2716   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2717   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2718   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2719   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2720   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2721   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2722   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2723   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2724   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2725   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2726   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2727   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2728   minorSize = 0;\r
2729   }\r
2730 }\r
2731 \r
2732 HBITMAP\r
2733 PieceBitmap(ChessSquare p, int kind)\r
2734 {\r
2735   if ((int) p >= (int) BlackPawn)\r
2736     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2737 \r
2738   return pieceBitmap[kind][(int) p];\r
2739 }\r
2740 \r
2741 /***************************************************************/\r
2742 \r
2743 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2744 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2745 /*\r
2746 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2747 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2748 */\r
2749 \r
2750 VOID\r
2751 SquareToPos(int row, int column, int * x, int * y)\r
2752 {\r
2753   if (flipView) {\r
2754     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2755     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2756   } else {\r
2757     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2758     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2759   }\r
2760 }\r
2761 \r
2762 VOID\r
2763 DrawCoordsOnDC(HDC hdc)\r
2764 {\r
2765   static char files[] = "0123456789012345678901221098765432109876543210";\r
2766   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2767   char str[2] = { NULLCHAR, NULLCHAR };\r
2768   int oldMode, oldAlign, x, y, start, i;\r
2769   HFONT oldFont;\r
2770   HBRUSH oldBrush;\r
2771 \r
2772   if (!appData.showCoords)\r
2773     return;\r
2774 \r
2775   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2776 \r
2777   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2778   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2779   oldAlign = GetTextAlign(hdc);\r
2780   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2781 \r
2782   y = boardRect.top + lineGap;\r
2783   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2784 \r
2785   if(border) {\r
2786     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2787     x += border - lineGap - 4; y += squareSize - 6;\r
2788   } else\r
2789   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2790   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2791     str[0] = files[start + i];\r
2792     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2793     y += squareSize + lineGap;\r
2794   }\r
2795 \r
2796   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2797 \r
2798   if(border) {\r
2799     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2800     x += -border + 4; y += border - squareSize + 6;\r
2801   } else\r
2802   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2803   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2804     str[0] = ranks[start + i];\r
2805     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2806     x += squareSize + lineGap;\r
2807   }    \r
2808 \r
2809   SelectObject(hdc, oldBrush);\r
2810   SetBkMode(hdc, oldMode);\r
2811   SetTextAlign(hdc, oldAlign);\r
2812   SelectObject(hdc, oldFont);\r
2813 }\r
2814 \r
2815 VOID\r
2816 DrawGridOnDC(HDC hdc)\r
2817 {\r
2818   HPEN oldPen;\r
2819  \r
2820   if (lineGap != 0) {\r
2821     oldPen = SelectObject(hdc, gridPen);\r
2822     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2823     SelectObject(hdc, oldPen);\r
2824   }\r
2825 }\r
2826 \r
2827 #define HIGHLIGHT_PEN 0\r
2828 #define PREMOVE_PEN   1\r
2829 \r
2830 VOID\r
2831 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2832 {\r
2833   int x1, y1;\r
2834   HPEN oldPen, hPen;\r
2835   if (lineGap == 0) return;\r
2836   if (flipView) {\r
2837     x1 = boardRect.left +\r
2838       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2839     y1 = boardRect.top +\r
2840       lineGap/2 + y * (squareSize + lineGap) + border;\r
2841   } else {\r
2842     x1 = boardRect.left +\r
2843       lineGap/2 + x * (squareSize + lineGap) + border;\r
2844     y1 = boardRect.top +\r
2845       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2846   }\r
2847   hPen = pen ? premovePen : highlightPen;\r
2848   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2849   MoveToEx(hdc, x1, y1, NULL);\r
2850   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2851   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2852   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2853   LineTo(hdc, x1, y1);\r
2854   SelectObject(hdc, oldPen);\r
2855 }\r
2856 \r
2857 VOID\r
2858 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2859 {\r
2860   int i;\r
2861   for (i=0; i<2; i++) {\r
2862     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2863       DrawHighlightOnDC(hdc, TRUE,\r
2864                         h->sq[i].x, h->sq[i].y,\r
2865                         pen);\r
2866   }\r
2867 }\r
2868 \r
2869 /* Note: sqcolor is used only in monoMode */\r
2870 /* Note that this code is largely duplicated in woptions.c,\r
2871    function DrawSampleSquare, so that needs to be updated too */\r
2872 VOID\r
2873 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2874 {\r
2875   HBITMAP oldBitmap;\r
2876   HBRUSH oldBrush;\r
2877   int tmpSize;\r
2878 \r
2879   if (appData.blindfold) return;\r
2880 \r
2881   /* [AS] Use font-based pieces if needed */\r
2882   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2883     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2884     CreatePiecesFromFont();\r
2885 \r
2886     if( fontBitmapSquareSize == squareSize ) {\r
2887         int index = TranslatePieceToFontPiece(piece);\r
2888 \r
2889         SelectObject( tmphdc, hPieceMask[ index ] );\r
2890 \r
2891       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2892         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2893       else\r
2894         BitBlt( hdc,\r
2895             x, y,\r
2896             squareSize, squareSize,\r
2897             tmphdc,\r
2898             0, 0,\r
2899             SRCAND );\r
2900 \r
2901         SelectObject( tmphdc, hPieceFace[ index ] );\r
2902 \r
2903       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2904         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2905       else\r
2906         BitBlt( hdc,\r
2907             x, y,\r
2908             squareSize, squareSize,\r
2909             tmphdc,\r
2910             0, 0,\r
2911             SRCPAINT );\r
2912 \r
2913         return;\r
2914     }\r
2915   }\r
2916 \r
2917   if (appData.monoMode) {\r
2918     SelectObject(tmphdc, PieceBitmap(piece, \r
2919       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2920     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2921            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2922   } else {\r
2923     HBRUSH xBrush = whitePieceBrush;\r
2924     tmpSize = squareSize;\r
2925     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2926     if(minorSize &&\r
2927         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2928          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2929       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2930       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2931       x += (squareSize - minorSize)>>1;\r
2932       y += squareSize - minorSize - 2;\r
2933       tmpSize = minorSize;\r
2934     }\r
2935     if (color || appData.allWhite ) {\r
2936       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2937       if( color )\r
2938               oldBrush = SelectObject(hdc, xBrush);\r
2939       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2940       if(appData.upsideDown && color==flipView)\r
2941         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2942       else\r
2943         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2944       /* Use black for outline of white pieces */\r
2945       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2946       if(appData.upsideDown && color==flipView)\r
2947         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2948       else\r
2949         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2950     } else if(appData.pieceDirectory[0]) {\r
2951       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2952       oldBrush = SelectObject(hdc, xBrush);\r
2953       if(appData.upsideDown && color==flipView)\r
2954         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2955       else\r
2956         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2957       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2958       if(appData.upsideDown && color==flipView)\r
2959         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2960       else\r
2961         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2962     } else {\r
2963       /* Use square color for details of black pieces */\r
2964       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2965       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2966       if(appData.upsideDown && !flipView)\r
2967         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2968       else\r
2969         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2970     }\r
2971     SelectObject(hdc, oldBrush);\r
2972     SelectObject(tmphdc, oldBitmap);\r
2973   }\r
2974 }\r
2975 \r
2976 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2977 int GetBackTextureMode( int algo )\r
2978 {\r
2979     int result = BACK_TEXTURE_MODE_DISABLED;\r
2980 \r
2981     switch( algo ) \r
2982     {\r
2983         case BACK_TEXTURE_MODE_PLAIN:\r
2984             result = 1; /* Always use identity map */\r
2985             break;\r
2986         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2987             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2988             break;\r
2989     }\r
2990 \r
2991     return result;\r
2992 }\r
2993 \r
2994 /* \r
2995     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2996     to handle redraws cleanly (as random numbers would always be different).\r
2997 */\r
2998 VOID RebuildTextureSquareInfo()\r
2999 {\r
3000     BITMAP bi;\r
3001     int lite_w = 0;\r
3002     int lite_h = 0;\r
3003     int dark_w = 0;\r
3004     int dark_h = 0;\r
3005     int row;\r
3006     int col;\r
3007 \r
3008     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3009 \r
3010     if( liteBackTexture != NULL ) {\r
3011         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3012             lite_w = bi.bmWidth;\r
3013             lite_h = bi.bmHeight;\r
3014         }\r
3015     }\r
3016 \r
3017     if( darkBackTexture != NULL ) {\r
3018         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3019             dark_w = bi.bmWidth;\r
3020             dark_h = bi.bmHeight;\r
3021         }\r
3022     }\r
3023 \r
3024     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3025         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3026             if( (col + row) & 1 ) {\r
3027                 /* Lite square */\r
3028                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3029                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3030                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3031                   else\r
3032                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3033                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3034                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3035                   else\r
3036                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3037                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3038                 }\r
3039             }\r
3040             else {\r
3041                 /* Dark square */\r
3042                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3043                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3044                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3045                   else\r
3046                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3047                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3048                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3049                   else\r
3050                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3051                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3052                 }\r
3053             }\r
3054         }\r
3055     }\r
3056 }\r
3057 \r
3058 /* [AS] Arrow highlighting support */\r
3059 \r
3060 static double A_WIDTH = 5; /* Width of arrow body */\r
3061 \r
3062 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3063 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3064 \r
3065 static double Sqr( double x )\r
3066 {\r
3067     return x*x;\r
3068 }\r
3069 \r
3070 static int Round( double x )\r
3071 {\r
3072     return (int) (x + 0.5);\r
3073 }\r
3074 \r
3075 /* Draw an arrow between two points using current settings */\r
3076 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3077 {\r
3078     POINT arrow[7];\r
3079     double dx, dy, j, k, x, y;\r
3080 \r
3081     if( d_x == s_x ) {\r
3082         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3083 \r
3084         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3085         arrow[0].y = s_y;\r
3086 \r
3087         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3088         arrow[1].y = d_y - h;\r
3089 \r
3090         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3091         arrow[2].y = d_y - h;\r
3092 \r
3093         arrow[3].x = d_x;\r
3094         arrow[3].y = d_y;\r
3095 \r
3096         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3097         arrow[5].y = d_y - h;\r
3098 \r
3099         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3100         arrow[4].y = d_y - h;\r
3101 \r
3102         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3103         arrow[6].y = s_y;\r
3104     }\r
3105     else if( d_y == s_y ) {\r
3106         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3107 \r
3108         arrow[0].x = s_x;\r
3109         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3110 \r
3111         arrow[1].x = d_x - w;\r
3112         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3113 \r
3114         arrow[2].x = d_x - w;\r
3115         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3116 \r
3117         arrow[3].x = d_x;\r
3118         arrow[3].y = d_y;\r
3119 \r
3120         arrow[5].x = d_x - w;\r
3121         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3122 \r
3123         arrow[4].x = d_x - w;\r
3124         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3125 \r
3126         arrow[6].x = s_x;\r
3127         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3128     }\r
3129     else {\r
3130         /* [AS] Needed a lot of paper for this! :-) */\r
3131         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3132         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3133   \r
3134         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3135 \r
3136         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3137 \r
3138         x = s_x;\r
3139         y = s_y;\r
3140 \r
3141         arrow[0].x = Round(x - j);\r
3142         arrow[0].y = Round(y + j*dx);\r
3143 \r
3144         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3145         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3146 \r
3147         if( d_x > s_x ) {\r
3148             x = (double) d_x - k;\r
3149             y = (double) d_y - k*dy;\r
3150         }\r
3151         else {\r
3152             x = (double) d_x + k;\r
3153             y = (double) d_y + k*dy;\r
3154         }\r
3155 \r
3156         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3157 \r
3158         arrow[6].x = Round(x - j);\r
3159         arrow[6].y = Round(y + j*dx);\r
3160 \r
3161         arrow[2].x = Round(arrow[6].x + 2*j);\r
3162         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3163 \r
3164         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3165         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3166 \r
3167         arrow[4].x = d_x;\r
3168         arrow[4].y = d_y;\r
3169 \r
3170         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3171         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3172     }\r
3173 \r
3174     Polygon( hdc, arrow, 7 );\r
3175 }\r
3176 \r
3177 /* [AS] Draw an arrow between two squares */\r
3178 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3179 {\r
3180     int s_x, s_y, d_x, d_y;\r
3181     HPEN hpen;\r
3182     HPEN holdpen;\r
3183     HBRUSH hbrush;\r
3184     HBRUSH holdbrush;\r
3185     LOGBRUSH stLB;\r
3186 \r
3187     if( s_col == d_col && s_row == d_row ) {\r
3188         return;\r
3189     }\r
3190 \r
3191     /* Get source and destination points */\r
3192     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3193     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3194 \r
3195     if( d_y > s_y ) {\r
3196         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3197     }\r
3198     else if( d_y < s_y ) {\r
3199         d_y += squareSize / 2 + squareSize / 4;\r
3200     }\r
3201     else {\r
3202         d_y += squareSize / 2;\r
3203     }\r
3204 \r
3205     if( d_x > s_x ) {\r
3206         d_x += squareSize / 2 - squareSize / 4;\r
3207     }\r
3208     else if( d_x < s_x ) {\r
3209         d_x += squareSize / 2 + squareSize / 4;\r
3210     }\r
3211     else {\r
3212         d_x += squareSize / 2;\r
3213     }\r
3214 \r
3215     s_x += squareSize / 2;\r
3216     s_y += squareSize / 2;\r
3217 \r
3218     /* Adjust width */\r
3219     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3220 \r
3221     /* Draw */\r
3222     stLB.lbStyle = BS_SOLID;\r
3223     stLB.lbColor = appData.highlightArrowColor;\r
3224     stLB.lbHatch = 0;\r
3225 \r
3226     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3227     holdpen = SelectObject( hdc, hpen );\r
3228     hbrush = CreateBrushIndirect( &stLB );\r
3229     holdbrush = SelectObject( hdc, hbrush );\r
3230 \r
3231     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3232 \r
3233     SelectObject( hdc, holdpen );\r
3234     SelectObject( hdc, holdbrush );\r
3235     DeleteObject( hpen );\r
3236     DeleteObject( hbrush );\r
3237 }\r
3238 \r
3239 BOOL HasHighlightInfo()\r
3240 {\r
3241     BOOL result = FALSE;\r
3242 \r
3243     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3244         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3245     {\r
3246         result = TRUE;\r
3247     }\r
3248 \r
3249     return result;\r
3250 }\r
3251 \r
3252 BOOL IsDrawArrowEnabled()\r
3253 {\r
3254     BOOL result = FALSE;\r
3255 \r
3256     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3257         result = TRUE;\r
3258     }\r
3259 \r
3260     return result;\r
3261 }\r
3262 \r
3263 VOID DrawArrowHighlight( HDC hdc )\r
3264 {\r
3265     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3266         DrawArrowBetweenSquares( hdc,\r
3267             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3268             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3269     }\r
3270 }\r
3271 \r
3272 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3273 {\r
3274     HRGN result = NULL;\r
3275 \r
3276     if( HasHighlightInfo() ) {\r
3277         int x1, y1, x2, y2;\r
3278         int sx, sy, dx, dy;\r
3279 \r
3280         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3281         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3282 \r
3283         sx = MIN( x1, x2 );\r
3284         sy = MIN( y1, y2 );\r
3285         dx = MAX( x1, x2 ) + squareSize;\r
3286         dy = MAX( y1, y2 ) + squareSize;\r
3287 \r
3288         result = CreateRectRgn( sx, sy, dx, dy );\r
3289     }\r
3290 \r
3291     return result;\r
3292 }\r
3293 \r
3294 /*\r
3295     Warning: this function modifies the behavior of several other functions. \r
3296     \r
3297     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3298     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3299     repaint is scattered all over the place, which is not good for features such as\r
3300     "arrow highlighting" that require a full repaint of the board.\r
3301 \r
3302     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3303     user interaction, when speed is not so important) but especially to avoid errors\r
3304     in the displayed graphics.\r
3305 \r
3306     In such patched places, I always try refer to this function so there is a single\r
3307     place to maintain knowledge.\r
3308     \r
3309     To restore the original behavior, just return FALSE unconditionally.\r
3310 */\r
3311 BOOL IsFullRepaintPreferrable()\r
3312 {\r
3313     BOOL result = FALSE;\r
3314 \r
3315     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3316         /* Arrow may appear on the board */\r
3317         result = TRUE;\r
3318     }\r
3319 \r
3320     return result;\r
3321 }\r
3322 \r
3323 /* \r
3324     This function is called by DrawPosition to know whether a full repaint must\r
3325     be forced or not.\r
3326 \r
3327     Only DrawPosition may directly call this function, which makes use of \r
3328     some state information. Other function should call DrawPosition specifying \r
3329     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3330 */\r
3331 BOOL DrawPositionNeedsFullRepaint()\r
3332 {\r
3333     BOOL result = FALSE;\r
3334 \r
3335     /* \r
3336         Probably a slightly better policy would be to trigger a full repaint\r
3337         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3338         but animation is fast enough that it's difficult to notice.\r
3339     */\r
3340     if( animInfo.piece == EmptySquare ) {\r
3341         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3342             result = TRUE;\r
3343         }\r
3344     }\r
3345 \r
3346     return result;\r
3347 }\r
3348 \r
3349 static HBITMAP borderBitmap;\r
3350 \r
3351 VOID\r
3352 DrawBackgroundOnDC(HDC hdc)\r
3353 {\r
3354   \r
3355   BITMAP bi;\r
3356   HDC tmphdc;\r
3357   HBITMAP hbm;\r
3358   static char oldBorder[MSG_SIZ];\r
3359   int w = 600, h = 600, mode;\r
3360 \r
3361   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3362     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3363     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3364   }\r
3365   if(borderBitmap == NULL) { // loading failed, use white\r
3366     FillRect( hdc, &boardRect, whitePieceBrush );\r
3367     return;\r
3368   }\r
3369   tmphdc = CreateCompatibleDC(hdc);\r
3370   hbm = SelectObject(tmphdc, borderBitmap);\r
3371   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3372             w = bi.bmWidth;\r
3373             h = bi.bmHeight;\r
3374   }\r
3375   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3376   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3377                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3378   SetStretchBltMode(hdc, mode);\r
3379   SelectObject(tmphdc, hbm);\r
3380   DeleteDC(tmphdc);\r
3381 }\r
3382 \r
3383 VOID\r
3384 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3385 {\r
3386   int row, column, x, y, square_color, piece_color;\r
3387   ChessSquare piece;\r
3388   HBRUSH oldBrush;\r
3389   HDC texture_hdc = NULL;\r
3390 \r
3391   /* [AS] Initialize background textures if needed */\r
3392   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3393       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3394       if( backTextureSquareSize != squareSize \r
3395        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3396           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3397           backTextureSquareSize = squareSize;\r
3398           RebuildTextureSquareInfo();\r
3399       }\r
3400 \r
3401       texture_hdc = CreateCompatibleDC( hdc );\r
3402   }\r
3403 \r
3404   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3405     for (column = 0; column < BOARD_WIDTH; column++) {\r
3406   \r
3407       SquareToPos(row, column, &x, &y);\r
3408 \r
3409       piece = board[row][column];\r
3410 \r
3411       square_color = ((column + row) % 2) == 1;\r
3412       if( gameInfo.variant == VariantXiangqi ) {\r
3413           square_color = !InPalace(row, column);\r
3414           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3415           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3416       }\r
3417       piece_color = (int) piece < (int) BlackPawn;\r
3418 \r
3419 \r
3420       /* [HGM] holdings file: light square or black */\r
3421       if(column == BOARD_LEFT-2) {\r
3422             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3423                 square_color = 1;\r
3424             else {\r
3425                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3426                 continue;\r
3427             }\r
3428       } else\r
3429       if(column == BOARD_RGHT + 1 ) {\r
3430             if( row < gameInfo.holdingsSize )\r
3431                 square_color = 1;\r
3432             else {\r
3433                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3434                 continue;\r
3435             }\r
3436       }\r
3437       if(column == BOARD_LEFT-1 ) /* left align */\r
3438             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3439       else if( column == BOARD_RGHT) /* right align */\r
3440             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3441       else\r
3442       if (appData.monoMode) {\r
3443         if (piece == EmptySquare) {\r
3444           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3445                  square_color ? WHITENESS : BLACKNESS);\r
3446         } else {\r
3447           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3448         }\r
3449       } \r
3450       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3451           /* [AS] Draw the square using a texture bitmap */\r
3452           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3453           int r = row, c = column; // [HGM] do not flip board in flipView\r
3454           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3455 \r
3456           DrawTile( x, y, \r
3457               squareSize, squareSize, \r
3458               hdc, \r
3459               texture_hdc,\r
3460               backTextureSquareInfo[r][c].mode,\r
3461               backTextureSquareInfo[r][c].x,\r
3462               backTextureSquareInfo[r][c].y );\r
3463 \r
3464           SelectObject( texture_hdc, hbm );\r
3465 \r
3466           if (piece != EmptySquare) {\r
3467               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3468           }\r
3469       }\r
3470       else {\r
3471         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3472 \r
3473         oldBrush = SelectObject(hdc, brush );\r
3474         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3475         SelectObject(hdc, oldBrush);\r
3476         if (piece != EmptySquare)\r
3477           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3478       }\r
3479     }\r
3480   }\r
3481 \r
3482   if( texture_hdc != NULL ) {\r
3483     DeleteDC( texture_hdc );\r
3484   }\r
3485 }\r
3486 \r
3487 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3488 void fputDW(FILE *f, int x)\r
3489 {\r
3490         fputc(x     & 255, f);\r
3491         fputc(x>>8  & 255, f);\r
3492         fputc(x>>16 & 255, f);\r
3493         fputc(x>>24 & 255, f);\r
3494 }\r
3495 \r
3496 #define MAX_CLIPS 200   /* more than enough */\r
3497 \r
3498 VOID\r
3499 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3500 {\r
3501 //  HBITMAP bufferBitmap;\r
3502   BITMAP bi;\r
3503 //  RECT Rect;\r
3504   HDC tmphdc;\r
3505   HBITMAP hbm;\r
3506   int w = 100, h = 50;\r
3507 \r
3508   if(logo == NULL) {\r
3509     if(!logoHeight) return;\r
3510     FillRect( hdc, &logoRect, whitePieceBrush );\r
3511   }\r
3512 //  GetClientRect(hwndMain, &Rect);\r
3513 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3514 //                                      Rect.bottom-Rect.top+1);\r
3515   tmphdc = CreateCompatibleDC(hdc);\r
3516   hbm = SelectObject(tmphdc, logo);\r
3517   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3518             w = bi.bmWidth;\r
3519             h = bi.bmHeight;\r
3520   }\r
3521   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3522                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3523   SelectObject(tmphdc, hbm);\r
3524   DeleteDC(tmphdc);\r
3525 }\r
3526 \r
3527 VOID\r
3528 DisplayLogos()\r
3529 {\r
3530   if(logoHeight) {\r
3531         HDC hdc = GetDC(hwndMain);\r
3532         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3533         if(appData.autoLogo) {\r
3534           \r
3535           switch(gameMode) { // pick logos based on game mode\r
3536             case IcsObserving:\r
3537                 whiteLogo = second.programLogo; // ICS logo\r
3538                 blackLogo = second.programLogo;\r
3539             default:\r
3540                 break;\r
3541             case IcsPlayingWhite:\r
3542                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3543                 blackLogo = second.programLogo; // ICS logo\r
3544                 break;\r
3545             case IcsPlayingBlack:\r
3546                 whiteLogo = second.programLogo; // ICS logo\r
3547                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3548                 break;\r
3549             case TwoMachinesPlay:\r
3550                 if(first.twoMachinesColor[0] == 'b') {\r
3551                     whiteLogo = second.programLogo;\r
3552                     blackLogo = first.programLogo;\r
3553                 }\r
3554                 break;\r
3555             case MachinePlaysWhite:\r
3556                 blackLogo = userLogo;\r
3557                 break;\r
3558             case MachinePlaysBlack:\r
3559                 whiteLogo = userLogo;\r
3560                 blackLogo = first.programLogo;\r
3561           }\r
3562         }\r
3563         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3564         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3565         ReleaseDC(hwndMain, hdc);\r
3566   }\r
3567 }\r
3568 \r
3569 void\r
3570 UpdateLogos(int display)\r
3571 { // called after loading new engine(s), in tourney or from menu\r
3572   LoadLogo(&first, 0, FALSE);\r
3573   LoadLogo(&second, 1, appData.icsActive);\r
3574   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3575   if(display) DisplayLogos();\r
3576 }\r
3577 \r
3578 static HDC hdcSeek;\r
3579 \r
3580 // [HGM] seekgraph\r
3581 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3582 {\r
3583     POINT stPt;\r
3584     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3585     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3586     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3587     SelectObject( hdcSeek, hp );\r
3588 }\r
3589 \r
3590 // front-end wrapper for drawing functions to do rectangles\r
3591 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3592 {\r
3593     HPEN hp;\r
3594     RECT rc;\r
3595 \r
3596     if (hdcSeek == NULL) {\r
3597     hdcSeek = GetDC(hwndMain);\r
3598       if (!appData.monoMode) {\r
3599         SelectPalette(hdcSeek, hPal, FALSE);\r
3600         RealizePalette(hdcSeek);\r
3601       }\r
3602     }\r
3603     hp = SelectObject( hdcSeek, gridPen );\r
3604     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3605     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3606     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3607     SelectObject( hdcSeek, hp );\r
3608 }\r
3609 \r
3610 // front-end wrapper for putting text in graph\r
3611 void DrawSeekText(char *buf, int x, int y)\r
3612 {\r
3613         SIZE stSize;\r
3614         SetBkMode( hdcSeek, TRANSPARENT );\r
3615         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3616         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3617 }\r
3618 \r
3619 void DrawSeekDot(int x, int y, int color)\r
3620 {\r
3621         int square = color & 0x80;\r
3622         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3623                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3624         color &= 0x7F;\r
3625         if(square)\r
3626             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3627                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3628         else\r
3629             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3630                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3631             SelectObject(hdcSeek, oldBrush);\r
3632 }\r
3633 \r
3634 void DrawSeekOpen()\r
3635 {\r
3636 }\r
3637 \r
3638 void DrawSeekClose()\r
3639 {\r
3640 }\r
3641 \r
3642 VOID\r
3643 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3644 {\r
3645   static Board lastReq[2], lastDrawn[2];\r
3646   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3647   static int lastDrawnFlipView = 0;\r
3648   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3649   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3650   HDC tmphdc;\r
3651   HDC hdcmem;\r
3652   HBITMAP bufferBitmap;\r
3653   HBITMAP oldBitmap;\r
3654   RECT Rect;\r
3655   HRGN clips[MAX_CLIPS];\r
3656   ChessSquare dragged_piece = EmptySquare;\r
3657   int nr = twoBoards*partnerUp;\r
3658 \r
3659   /* I'm undecided on this - this function figures out whether a full\r
3660    * repaint is necessary on its own, so there's no real reason to have the\r
3661    * caller tell it that.  I think this can safely be set to FALSE - but\r
3662    * if we trust the callers not to request full repaints unnessesarily, then\r
3663    * we could skip some clipping work.  In other words, only request a full\r
3664    * redraw when the majority of pieces have changed positions (ie. flip, \r
3665    * gamestart and similar)  --Hawk\r
3666    */\r
3667   Boolean fullrepaint = repaint;\r
3668 \r
3669   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3670 \r
3671   if( DrawPositionNeedsFullRepaint() ) {\r
3672       fullrepaint = TRUE;\r
3673   }\r
3674 \r
3675   if (board == NULL) {\r
3676     if (!lastReqValid[nr]) {\r
3677       return;\r
3678     }\r
3679     board = lastReq[nr];\r
3680   } else {\r
3681     CopyBoard(lastReq[nr], board);\r
3682     lastReqValid[nr] = 1;\r
3683   }\r
3684 \r
3685   if (doingSizing) {\r
3686     return;\r
3687   }\r
3688 \r
3689   if (IsIconic(hwndMain)) {\r
3690     return;\r
3691   }\r
3692 \r
3693   if (hdc == NULL) {\r
3694     hdc = GetDC(hwndMain);\r
3695     if (!appData.monoMode) {\r
3696       SelectPalette(hdc, hPal, FALSE);\r
3697       RealizePalette(hdc);\r
3698     }\r
3699     releaseDC = TRUE;\r
3700   } else {\r
3701     releaseDC = FALSE;\r
3702   }\r
3703 \r
3704   /* Create some work-DCs */\r
3705   hdcmem = CreateCompatibleDC(hdc);\r
3706   tmphdc = CreateCompatibleDC(hdc);\r
3707 \r
3708   /* If dragging is in progress, we temporarely remove the piece */\r
3709   /* [HGM] or temporarily decrease count if stacked              */\r
3710   /*       !! Moved to before board compare !!                   */\r
3711   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3712     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3713     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3714             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3715         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3716     } else \r
3717     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3718             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3719         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3720     } else \r
3721         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3722   }\r
3723 \r
3724   /* Figure out which squares need updating by comparing the \r
3725    * newest board with the last drawn board and checking if\r
3726    * flipping has changed.\r
3727    */\r
3728   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3729     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3730       for (column = 0; column < BOARD_WIDTH; column++) {\r
3731         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3732           SquareToPos(row, column, &x, &y);\r
3733           clips[num_clips++] =\r
3734             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3735         }\r
3736       }\r
3737     }\r
3738    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3739     for (i=0; i<2; i++) {\r
3740       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3741           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3742         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3743             lastDrawnHighlight.sq[i].y >= 0) {\r
3744           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3745                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3746           clips[num_clips++] =\r
3747             CreateRectRgn(x - lineGap, y - lineGap, \r
3748                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3749         }\r
3750         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3751           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3752           clips[num_clips++] =\r
3753             CreateRectRgn(x - lineGap, y - lineGap, \r
3754                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3755         }\r
3756       }\r
3757     }\r
3758     for (i=0; i<2; i++) {\r
3759       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3760           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3761         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3762             lastDrawnPremove.sq[i].y >= 0) {\r
3763           SquareToPos(lastDrawnPremove.sq[i].y,\r
3764                       lastDrawnPremove.sq[i].x, &x, &y);\r
3765           clips[num_clips++] =\r
3766             CreateRectRgn(x - lineGap, y - lineGap, \r
3767                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3768         }\r
3769         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3770             premoveHighlightInfo.sq[i].y >= 0) {\r
3771           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3772                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3773           clips[num_clips++] =\r
3774             CreateRectRgn(x - lineGap, y - lineGap, \r
3775                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3776         }\r
3777       }\r
3778     }\r
3779    } else { // nr == 1\r
3780         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3781         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3782         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3783         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3784       for (i=0; i<2; i++) {\r
3785         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3786             partnerHighlightInfo.sq[i].y >= 0) {\r
3787           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3788                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3789           clips[num_clips++] =\r
3790             CreateRectRgn(x - lineGap, y - lineGap, \r
3791                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3792         }\r
3793         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3794             oldPartnerHighlight.sq[i].y >= 0) {\r
3795           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3796                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3797           clips[num_clips++] =\r
3798             CreateRectRgn(x - lineGap, y - lineGap, \r
3799                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3800         }\r
3801       }\r
3802    }\r
3803   } else {\r
3804     fullrepaint = TRUE;\r
3805   }\r
3806 \r
3807   /* Create a buffer bitmap - this is the actual bitmap\r
3808    * being written to.  When all the work is done, we can\r
3809    * copy it to the real DC (the screen).  This avoids\r
3810    * the problems with flickering.\r
3811    */\r
3812   GetClientRect(hwndMain, &Rect);\r
3813   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3814                                         Rect.bottom-Rect.top+1);\r
3815   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3816   if (!appData.monoMode) {\r
3817     SelectPalette(hdcmem, hPal, FALSE);\r
3818   }\r
3819 \r
3820   /* Create clips for dragging */\r
3821   if (!fullrepaint) {\r
3822     if (dragInfo.from.x >= 0) {\r
3823       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3824       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3825     }\r
3826     if (dragInfo.start.x >= 0) {\r
3827       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3828       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3829     }\r
3830     if (dragInfo.pos.x >= 0) {\r
3831       x = dragInfo.pos.x - squareSize / 2;\r
3832       y = dragInfo.pos.y - squareSize / 2;\r
3833       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3834     }\r
3835     if (dragInfo.lastpos.x >= 0) {\r
3836       x = dragInfo.lastpos.x - squareSize / 2;\r
3837       y = dragInfo.lastpos.y - squareSize / 2;\r
3838       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3839     }\r
3840   }\r
3841 \r
3842   /* Are we animating a move?  \r
3843    * If so, \r
3844    *   - remove the piece from the board (temporarely)\r
3845    *   - calculate the clipping region\r
3846    */\r
3847   if (!fullrepaint) {\r
3848     if (animInfo.piece != EmptySquare) {\r
3849       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3850       x = boardRect.left + animInfo.lastpos.x;\r
3851       y = boardRect.top + animInfo.lastpos.y;\r
3852       x2 = boardRect.left + animInfo.pos.x;\r
3853       y2 = boardRect.top + animInfo.pos.y;\r
3854       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3855       /* Slight kludge.  The real problem is that after AnimateMove is\r
3856          done, the position on the screen does not match lastDrawn.\r
3857          This currently causes trouble only on e.p. captures in\r
3858          atomic, where the piece moves to an empty square and then\r
3859          explodes.  The old and new positions both had an empty square\r
3860          at the destination, but animation has drawn a piece there and\r
3861          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3862       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3863     }\r
3864   }\r
3865 \r
3866   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3867   if (num_clips == 0)\r
3868     fullrepaint = TRUE;\r
3869 \r
3870   /* Set clipping on the memory DC */\r
3871   if (!fullrepaint) {\r
3872     SelectClipRgn(hdcmem, clips[0]);\r
3873     for (x = 1; x < num_clips; x++) {\r
3874       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3875         abort();  // this should never ever happen!\r
3876     }\r
3877   }\r
3878 \r
3879   /* Do all the drawing to the memory DC */\r
3880   if(explodeInfo.radius) { // [HGM] atomic\r
3881         HBRUSH oldBrush;\r
3882         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3883         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3884         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3885         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3886         x += squareSize/2;\r
3887         y += squareSize/2;\r
3888         if(!fullrepaint) {\r
3889           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3890           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3891         }\r
3892         DrawGridOnDC(hdcmem);\r
3893         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3894         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3895         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3896         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3897         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3898         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3899         SelectObject(hdcmem, oldBrush);\r
3900   } else {\r
3901     if(border) DrawBackgroundOnDC(hdcmem);\r
3902     DrawGridOnDC(hdcmem);\r
3903     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3904         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3905         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3906     } else {\r
3907         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3908         oldPartnerHighlight = partnerHighlightInfo;\r
3909     }\r
3910     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3911   }\r
3912   if(nr == 0) // [HGM] dual: markers only on left board\r
3913   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3914     for (column = 0; column < BOARD_WIDTH; column++) {\r
3915         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3916             HBRUSH oldBrush = SelectObject(hdcmem, \r
3917                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3918             SquareToPos(row, column, &x, &y);\r
3919             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3920                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3921             SelectObject(hdcmem, oldBrush);\r
3922         }\r
3923     }\r
3924   }\r
3925 \r
3926   if( appData.highlightMoveWithArrow ) {\r
3927     DrawArrowHighlight(hdcmem);\r
3928   }\r
3929 \r
3930   DrawCoordsOnDC(hdcmem);\r
3931 \r
3932   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3933                  /* to make sure lastDrawn contains what is actually drawn */\r
3934 \r
3935   /* Put the dragged piece back into place and draw it (out of place!) */\r
3936     if (dragged_piece != EmptySquare) {\r
3937     /* [HGM] or restack */\r
3938     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3939                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3940     else\r
3941     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3942                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3943     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3944     x = dragInfo.pos.x - squareSize / 2;\r
3945     y = dragInfo.pos.y - squareSize / 2;\r
3946     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3947                   ((int) dragInfo.piece < (int) BlackPawn), \r
3948                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3949   }   \r
3950   \r
3951   /* Put the animated piece back into place and draw it */\r
3952   if (animInfo.piece != EmptySquare) {\r
3953     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3954     x = boardRect.left + animInfo.pos.x;\r
3955     y = boardRect.top + animInfo.pos.y;\r
3956     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3957                   ((int) animInfo.piece < (int) BlackPawn),\r
3958                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3959   }\r
3960 \r
3961   /* Release the bufferBitmap by selecting in the old bitmap \r
3962    * and delete the memory DC\r
3963    */\r
3964   SelectObject(hdcmem, oldBitmap);\r
3965   DeleteDC(hdcmem);\r
3966 \r
3967   /* Set clipping on the target DC */\r
3968   if (!fullrepaint) {\r
3969     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3970         RECT rect;\r
3971         GetRgnBox(clips[x], &rect);\r
3972         DeleteObject(clips[x]);\r
3973         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3974                           rect.right + wpMain.width/2, rect.bottom);\r
3975     }\r
3976     SelectClipRgn(hdc, clips[0]);\r
3977     for (x = 1; x < num_clips; x++) {\r
3978       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3979         abort();   // this should never ever happen!\r
3980     } \r
3981   }\r
3982 \r
3983   /* Copy the new bitmap onto the screen in one go.\r
3984    * This way we avoid any flickering\r
3985    */\r
3986   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3987   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3988          boardRect.right - boardRect.left,\r
3989          boardRect.bottom - boardRect.top,\r
3990          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3991   if(saveDiagFlag) { \r
3992     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3993     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3994 \r
3995     GetObject(bufferBitmap, sizeof(b), &b);\r
3996     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3997         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3998         bih.biWidth = b.bmWidth;\r
3999         bih.biHeight = b.bmHeight;\r
4000         bih.biPlanes = 1;\r
4001         bih.biBitCount = b.bmBitsPixel;\r
4002         bih.biCompression = 0;\r
4003         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4004         bih.biXPelsPerMeter = 0;\r
4005         bih.biYPelsPerMeter = 0;\r
4006         bih.biClrUsed = 0;\r
4007         bih.biClrImportant = 0;\r
4008 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4009 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4010         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4011 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4012 \r
4013         wb = b.bmWidthBytes;\r
4014         // count colors\r
4015         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4016                 int k = ((int*) pData)[i];\r
4017                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4018                 if(j >= 16) break;\r
4019                 color[j] = k;\r
4020                 if(j >= nrColors) nrColors = j+1;\r
4021         }\r
4022         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4023                 INT p = 0;\r
4024                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4025                     for(w=0; w<(wb>>2); w+=2) {\r
4026                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4027                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4028                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4029                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4030                         pData[p++] = m | j<<4;\r
4031                     }\r
4032                     while(p&3) pData[p++] = 0;\r
4033                 }\r
4034                 fac = 3;\r
4035                 wb = ((wb+31)>>5)<<2;\r
4036         }\r
4037         // write BITMAPFILEHEADER\r
4038         fprintf(diagFile, "BM");\r
4039         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4040         fputDW(diagFile, 0);\r
4041         fputDW(diagFile, 0x36 + (fac?64:0));\r
4042         // write BITMAPINFOHEADER\r
4043         fputDW(diagFile, 40);\r
4044         fputDW(diagFile, b.bmWidth);\r
4045         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4046         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4047         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4048         fputDW(diagFile, 0);\r
4049         fputDW(diagFile, 0);\r
4050         fputDW(diagFile, 0);\r
4051         fputDW(diagFile, 0);\r
4052         fputDW(diagFile, 0);\r
4053         fputDW(diagFile, 0);\r
4054         // write color table\r
4055         if(fac)\r
4056         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4057         // write bitmap data\r
4058         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4059                 fputc(pData[i], diagFile);\r
4060         free(pData);\r
4061      }\r
4062   }\r
4063 \r
4064   SelectObject(tmphdc, oldBitmap);\r
4065 \r
4066   /* Massive cleanup */\r
4067   for (x = 0; x < num_clips; x++)\r
4068     DeleteObject(clips[x]);\r
4069 \r
4070   DeleteDC(tmphdc);\r
4071   DeleteObject(bufferBitmap);\r
4072 \r
4073   if (releaseDC) \r
4074     ReleaseDC(hwndMain, hdc);\r
4075   \r
4076   if (lastDrawnFlipView != flipView && nr == 0) {\r
4077     if (flipView)\r
4078       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4079     else\r
4080       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4081   }\r
4082 \r
4083 /*  CopyBoard(lastDrawn, board);*/\r
4084   lastDrawnHighlight = highlightInfo;\r
4085   lastDrawnPremove   = premoveHighlightInfo;\r
4086   lastDrawnFlipView = flipView;\r
4087   lastDrawnValid[nr] = 1;\r
4088 }\r
4089 \r
4090 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4091 int\r
4092 SaveDiagram(f)\r
4093      FILE *f;\r
4094 {\r
4095     saveDiagFlag = 1; diagFile = f;\r
4096     HDCDrawPosition(NULL, TRUE, NULL);\r
4097     saveDiagFlag = 0;\r
4098 \r
4099     fclose(f);\r
4100     return TRUE;\r
4101 }\r
4102 \r
4103 \r
4104 /*---------------------------------------------------------------------------*\\r
4105 | CLIENT PAINT PROCEDURE\r
4106 |   This is the main event-handler for the WM_PAINT message.\r
4107 |\r
4108 \*---------------------------------------------------------------------------*/\r
4109 VOID\r
4110 PaintProc(HWND hwnd)\r
4111 {\r
4112   HDC         hdc;\r
4113   PAINTSTRUCT ps;\r
4114   HFONT       oldFont;\r
4115 \r
4116   if((hdc = BeginPaint(hwnd, &ps))) {\r
4117     if (IsIconic(hwnd)) {\r
4118       DrawIcon(hdc, 2, 2, iconCurrent);\r
4119     } else {\r
4120       if (!appData.monoMode) {\r
4121         SelectPalette(hdc, hPal, FALSE);\r
4122         RealizePalette(hdc);\r
4123       }\r
4124       HDCDrawPosition(hdc, 1, NULL);\r
4125       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4126         flipView = !flipView; partnerUp = !partnerUp;\r
4127         HDCDrawPosition(hdc, 1, NULL);\r
4128         flipView = !flipView; partnerUp = !partnerUp;\r
4129       }\r
4130       oldFont =\r
4131         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4132       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4133                  ETO_CLIPPED|ETO_OPAQUE,\r
4134                  &messageRect, messageText, strlen(messageText), NULL);\r
4135       SelectObject(hdc, oldFont);\r
4136       DisplayBothClocks();\r
4137       DisplayLogos();\r
4138     }\r
4139     EndPaint(hwnd,&ps);\r
4140   }\r
4141 \r
4142   return;\r
4143 }\r
4144 \r
4145 \r
4146 /*\r
4147  * If the user selects on a border boundary, return -1; if off the board,\r
4148  *   return -2.  Otherwise map the event coordinate to the square.\r
4149  * The offset boardRect.left or boardRect.top must already have been\r
4150  *   subtracted from x.\r
4151  */\r
4152 int EventToSquare(x, limit)\r
4153      int x, limit;\r
4154 {\r
4155   if (x <= border)\r
4156     return -2;\r
4157   if (x < lineGap + border)\r
4158     return -1;\r
4159   x -= lineGap + border;\r
4160   if ((x % (squareSize + lineGap)) >= squareSize)\r
4161     return -1;\r
4162   x /= (squareSize + lineGap);\r
4163     if (x >= limit)\r
4164     return -2;\r
4165   return x;\r
4166 }\r
4167 \r
4168 typedef struct {\r
4169   char piece;\r
4170   int command;\r
4171   char* name;\r
4172 } DropEnable;\r
4173 \r
4174 DropEnable dropEnables[] = {\r
4175   { 'P', DP_Pawn, N_("Pawn") },\r
4176   { 'N', DP_Knight, N_("Knight") },\r
4177   { 'B', DP_Bishop, N_("Bishop") },\r
4178   { 'R', DP_Rook, N_("Rook") },\r
4179   { 'Q', DP_Queen, N_("Queen") },\r
4180 };\r
4181 \r
4182 VOID\r
4183 SetupDropMenu(HMENU hmenu)\r
4184 {\r
4185   int i, count, enable;\r
4186   char *p;\r
4187   extern char white_holding[], black_holding[];\r
4188   char item[MSG_SIZ];\r
4189 \r
4190   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4191     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4192                dropEnables[i].piece);\r
4193     count = 0;\r
4194     while (p && *p++ == dropEnables[i].piece) count++;\r
4195       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4196     enable = count > 0 || !appData.testLegality\r
4197       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4198                       && !appData.icsActive);\r
4199     ModifyMenu(hmenu, dropEnables[i].command,\r
4200                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4201                dropEnables[i].command, item);\r
4202   }\r
4203 }\r
4204 \r
4205 void DragPieceBegin(int x, int y, Boolean instantly)\r
4206 {\r
4207       dragInfo.lastpos.x = boardRect.left + x;\r
4208       dragInfo.lastpos.y = boardRect.top + y;\r
4209       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4210       dragInfo.from.x = fromX;\r
4211       dragInfo.from.y = fromY;\r
4212       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4213       dragInfo.start = dragInfo.from;\r
4214       SetCapture(hwndMain);\r
4215 }\r
4216 \r
4217 void DragPieceEnd(int x, int y)\r
4218 {\r
4219     ReleaseCapture();\r
4220     dragInfo.start.x = dragInfo.start.y = -1;\r
4221     dragInfo.from = dragInfo.start;\r
4222     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4223 }\r
4224 \r
4225 void ChangeDragPiece(ChessSquare piece)\r
4226 {\r
4227     dragInfo.piece = piece;\r
4228 }\r
4229 \r
4230 /* Event handler for mouse messages */\r
4231 VOID\r
4232 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4233 {\r
4234   int x, y, menuNr;\r
4235   POINT pt;\r
4236   static int recursive = 0;\r
4237   HMENU hmenu;\r
4238   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4239 \r
4240   if (recursive) {\r
4241     if (message == WM_MBUTTONUP) {\r
4242       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4243          to the middle button: we simulate pressing the left button too!\r
4244          */\r
4245       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4246       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4247     }\r
4248     return;\r
4249   }\r
4250   recursive++;\r
4251   \r
4252   pt.x = LOWORD(lParam);\r
4253   pt.y = HIWORD(lParam);\r
4254   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4255   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4256   if (!flipView && y >= 0) {\r
4257     y = BOARD_HEIGHT - 1 - y;\r
4258   }\r
4259   if (flipView && x >= 0) {\r
4260     x = BOARD_WIDTH - 1 - x;\r
4261   }\r
4262 \r
4263   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4264   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4265 \r
4266   switch (message) {\r
4267   case WM_LBUTTONDOWN:\r
4268       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4269         ClockClick(flipClock); break;\r
4270       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4271         ClockClick(!flipClock); break;\r
4272       }\r
4273       dragInfo.start.x = dragInfo.start.y = -1;\r
4274       dragInfo.from = dragInfo.start;\r
4275     if(fromX == -1 && frozen) { // not sure where this is for\r
4276                 fromX = fromY = -1; \r
4277       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4278       break;\r
4279     }\r
4280       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4281       DrawPosition(TRUE, NULL);\r
4282     break;\r
4283 \r
4284   case WM_LBUTTONUP:\r
4285       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4286       DrawPosition(TRUE, NULL);\r
4287     break;\r
4288 \r
4289   case WM_MOUSEMOVE:\r
4290     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4291     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4292     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4293     if ((appData.animateDragging || appData.highlightDragging)\r
4294         && (wParam & MK_LBUTTON)\r
4295         && dragInfo.from.x >= 0) \r
4296     {\r
4297       BOOL full_repaint = FALSE;\r
4298 \r
4299       if (appData.animateDragging) {\r
4300         dragInfo.pos = pt;\r
4301       }\r
4302       if (appData.highlightDragging) {\r
4303         SetHighlights(fromX, fromY, x, y);\r
4304         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4305             full_repaint = TRUE;\r
4306         }\r
4307       }\r
4308       \r
4309       DrawPosition( full_repaint, NULL);\r
4310       \r
4311       dragInfo.lastpos = dragInfo.pos;\r
4312     }\r
4313     break;\r
4314 \r
4315   case WM_MOUSEWHEEL: // [DM]\r
4316     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4317        /* Mouse Wheel is being rolled forward\r
4318         * Play moves forward\r
4319         */\r
4320        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4321                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4322        /* Mouse Wheel is being rolled backward\r
4323         * Play moves backward\r
4324         */\r
4325        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4326                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4327     }\r
4328     break;\r
4329 \r
4330   case WM_MBUTTONUP:\r
4331   case WM_RBUTTONUP:\r
4332     ReleaseCapture();\r
4333     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4334     break;\r
4335  \r
4336   case WM_MBUTTONDOWN:\r
4337   case WM_RBUTTONDOWN:\r
4338     ErrorPopDown();\r
4339     ReleaseCapture();\r
4340     fromX = fromY = -1;\r
4341     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4342     dragInfo.start.x = dragInfo.start.y = -1;\r
4343     dragInfo.from = dragInfo.start;\r
4344     dragInfo.lastpos = dragInfo.pos;\r
4345     if (appData.highlightDragging) {\r
4346       ClearHighlights();\r
4347     }\r
4348     if(y == -2) {\r
4349       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4350       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4351           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4352       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4353           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4354       }\r
4355       break;\r
4356     }\r
4357     DrawPosition(TRUE, NULL);\r
4358 \r
4359     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4360     switch (menuNr) {\r
4361     case 0:\r
4362       if (message == WM_MBUTTONDOWN) {\r
4363         buttonCount = 3;  /* even if system didn't think so */\r
4364         if (wParam & MK_SHIFT) \r
4365           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4366         else\r
4367           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4368       } else { /* message == WM_RBUTTONDOWN */\r
4369         /* Just have one menu, on the right button.  Windows users don't\r
4370            think to try the middle one, and sometimes other software steals\r
4371            it, or it doesn't really exist. */\r
4372         if(gameInfo.variant != VariantShogi)\r
4373             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4374         else\r
4375             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4376       }\r
4377       break;\r
4378     case 2:\r
4379       SetCapture(hwndMain);\r
4380       break;\r
4381     case 1:\r
4382       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4383       SetupDropMenu(hmenu);\r
4384       MenuPopup(hwnd, pt, hmenu, -1);\r
4385     default:\r
4386       break;\r
4387     }\r
4388     break;\r
4389   }\r
4390 \r
4391   recursive--;\r
4392 }\r
4393 \r
4394 /* Preprocess messages for buttons in main window */\r
4395 LRESULT CALLBACK\r
4396 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4397 {\r
4398   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4399   int i, dir;\r
4400 \r
4401   for (i=0; i<N_BUTTONS; i++) {\r
4402     if (buttonDesc[i].id == id) break;\r
4403   }\r
4404   if (i == N_BUTTONS) return 0;\r
4405   switch (message) {\r
4406   case WM_KEYDOWN:\r
4407     switch (wParam) {\r
4408     case VK_LEFT:\r
4409     case VK_RIGHT:\r
4410       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4411       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4412       return TRUE;\r
4413     }\r
4414     break;\r
4415   case WM_CHAR:\r
4416     switch (wParam) {\r
4417     case '\r':\r
4418       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4419       return TRUE;\r
4420     default:\r
4421       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4422         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4423         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4424         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4425         SetFocus(h);\r
4426         SendMessage(h, WM_CHAR, wParam, lParam);\r
4427         return TRUE;\r
4428       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4429         TypeInEvent((char)wParam);\r
4430       }\r
4431       break;\r
4432     }\r
4433     break;\r
4434   }\r
4435   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4436 }\r
4437 \r
4438 /* Process messages for Promotion dialog box */\r
4439 LRESULT CALLBACK\r
4440 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4441 {\r
4442   char promoChar;\r
4443 \r
4444   switch (message) {\r
4445   case WM_INITDIALOG: /* message: initialize dialog box */\r
4446     /* Center the dialog over the application window */\r
4447     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4448     Translate(hDlg, DLG_PromotionKing);\r
4449     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4450       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4451        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4452        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4453                SW_SHOW : SW_HIDE);\r
4454     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4455     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4456        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4457          PieceToChar(WhiteAngel) != '~') ||\r
4458         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4459          PieceToChar(BlackAngel) != '~')   ) ?\r
4460                SW_SHOW : SW_HIDE);\r
4461     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4462        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4463          PieceToChar(WhiteMarshall) != '~') ||\r
4464         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4465          PieceToChar(BlackMarshall) != '~')   ) ?\r
4466                SW_SHOW : SW_HIDE);\r
4467     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4468     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4469        gameInfo.variant != VariantShogi ?\r
4470                SW_SHOW : SW_HIDE);\r
4471     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4472        gameInfo.variant != VariantShogi ?\r
4473                SW_SHOW : SW_HIDE);\r
4474     if(gameInfo.variant == VariantShogi) {\r
4475         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4476         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4477         SetWindowText(hDlg, "Promote?");\r
4478     }\r
4479     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4480        gameInfo.variant == VariantSuper ?\r
4481                SW_SHOW : SW_HIDE);\r
4482     return TRUE;\r
4483 \r
4484   case WM_COMMAND: /* message: received a command */\r
4485     switch (LOWORD(wParam)) {\r
4486     case IDCANCEL:\r
4487       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4488       ClearHighlights();\r
4489       DrawPosition(FALSE, NULL);\r
4490       return TRUE;\r
4491     case PB_King:\r
4492       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4493       break;\r
4494     case PB_Queen:\r
4495       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4496       break;\r
4497     case PB_Rook:\r
4498       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4499       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4500       break;\r
4501     case PB_Bishop:\r
4502       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4503       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4504       break;\r
4505     case PB_Chancellor:\r
4506       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4507       break;\r
4508     case PB_Archbishop:\r
4509       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4510       break;\r
4511     case PB_Knight:\r
4512       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4513       break;\r
4514     default:\r
4515       return FALSE;\r
4516     }\r
4517     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4518     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4519     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4520     fromX = fromY = -1;\r
4521     if (!appData.highlightLastMove) {\r
4522       ClearHighlights();\r
4523       DrawPosition(FALSE, NULL);\r
4524     }\r
4525     return TRUE;\r
4526   }\r
4527   return FALSE;\r
4528 }\r
4529 \r
4530 /* Pop up promotion dialog */\r
4531 VOID\r
4532 PromotionPopup(HWND hwnd)\r
4533 {\r
4534   FARPROC lpProc;\r
4535 \r
4536   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4537   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4538     hwnd, (DLGPROC)lpProc);\r
4539   FreeProcInstance(lpProc);\r
4540 }\r
4541 \r
4542 void\r
4543 PromotionPopUp()\r
4544 {\r
4545   DrawPosition(TRUE, NULL);\r
4546   PromotionPopup(hwndMain);\r
4547 }\r
4548 \r
4549 VOID\r
4550 LoadGameDialog(HWND hwnd, char* title)\r
4551 {\r
4552   UINT number = 0;\r
4553   FILE *f;\r
4554   char fileTitle[MSG_SIZ];\r
4555   f = OpenFileDialog(hwnd, "rb", "",\r
4556                      appData.oldSaveStyle ? "gam" : "pgn",\r
4557                      GAME_FILT,\r
4558                      title, &number, fileTitle, NULL);\r
4559   if (f != NULL) {\r
4560     cmailMsgLoaded = FALSE;\r
4561     if (number == 0) {\r
4562       int error = GameListBuild(f);\r
4563       if (error) {\r
4564         DisplayError(_("Cannot build game list"), error);\r
4565       } else if (!ListEmpty(&gameList) &&\r
4566                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4567         GameListPopUp(f, fileTitle);\r
4568         return;\r
4569       }\r
4570       GameListDestroy();\r
4571       number = 1;\r
4572     }\r
4573     LoadGame(f, number, fileTitle, FALSE);\r
4574   }\r
4575 }\r
4576 \r
4577 int get_term_width()\r
4578 {\r
4579     HDC hdc;\r
4580     TEXTMETRIC tm;\r
4581     RECT rc;\r
4582     HFONT hfont, hold_font;\r
4583     LOGFONT lf;\r
4584     HWND hText;\r
4585 \r
4586     if (hwndConsole)\r
4587         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4588     else\r
4589         return 79;\r
4590 \r
4591     // get the text metrics\r
4592     hdc = GetDC(hText);\r
4593     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4594     if (consoleCF.dwEffects & CFE_BOLD)\r
4595         lf.lfWeight = FW_BOLD;\r
4596     if (consoleCF.dwEffects & CFE_ITALIC)\r
4597         lf.lfItalic = TRUE;\r
4598     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4599         lf.lfStrikeOut = TRUE;\r
4600     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4601         lf.lfUnderline = TRUE;\r
4602     hfont = CreateFontIndirect(&lf);\r
4603     hold_font = SelectObject(hdc, hfont);\r
4604     GetTextMetrics(hdc, &tm);\r
4605     SelectObject(hdc, hold_font);\r
4606     DeleteObject(hfont);\r
4607     ReleaseDC(hText, hdc);\r
4608 \r
4609     // get the rectangle\r
4610     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4611 \r
4612     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4613 }\r
4614 \r
4615 void UpdateICSWidth(HWND hText)\r
4616 {\r
4617     LONG old_width, new_width;\r
4618 \r
4619     new_width = get_term_width(hText, FALSE);\r
4620     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4621     if (new_width != old_width)\r
4622     {\r
4623         ics_update_width(new_width);\r
4624         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4625     }\r
4626 }\r
4627 \r
4628 VOID\r
4629 ChangedConsoleFont()\r
4630 {\r
4631   CHARFORMAT cfmt;\r
4632   CHARRANGE tmpsel, sel;\r
4633   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4634   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4635   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4636   PARAFORMAT paraf;\r
4637 \r
4638   cfmt.cbSize = sizeof(CHARFORMAT);\r
4639   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4640     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4641                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4642   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4643    * size.  This was undocumented in the version of MSVC++ that I had\r
4644    * when I wrote the code, but is apparently documented now.\r
4645    */\r
4646   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4647   cfmt.bCharSet = f->lf.lfCharSet;\r
4648   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4649   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4650   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4651   /* Why are the following seemingly needed too? */\r
4652   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4653   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4654   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4655   tmpsel.cpMin = 0;\r
4656   tmpsel.cpMax = -1; /*999999?*/\r
4657   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4658   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4659   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4660    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4661    */\r
4662   paraf.cbSize = sizeof(paraf);\r
4663   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4664   paraf.dxStartIndent = 0;\r
4665   paraf.dxOffset = WRAP_INDENT;\r
4666   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4667   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4668   UpdateICSWidth(hText);\r
4669 }\r
4670 \r
4671 /*---------------------------------------------------------------------------*\\r
4672  *\r
4673  * Window Proc for main window\r
4674  *\r
4675 \*---------------------------------------------------------------------------*/\r
4676 \r
4677 /* Process messages for main window, etc. */\r
4678 LRESULT CALLBACK\r
4679 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4680 {\r
4681   FARPROC lpProc;\r
4682   int wmId, wmEvent;\r
4683   char *defName;\r
4684   FILE *f;\r
4685   UINT number;\r
4686   char fileTitle[MSG_SIZ];\r
4687   static SnapData sd;\r
4688   static int peek=0;\r
4689 \r
4690   switch (message) {\r
4691 \r
4692   case WM_PAINT: /* message: repaint portion of window */\r
4693     PaintProc(hwnd);\r
4694     break;\r
4695 \r
4696   case WM_ERASEBKGND:\r
4697     if (IsIconic(hwnd)) {\r
4698       /* Cheat; change the message */\r
4699       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4700     } else {\r
4701       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4702     }\r
4703     break;\r
4704 \r
4705   case WM_LBUTTONDOWN:\r
4706   case WM_MBUTTONDOWN:\r
4707   case WM_RBUTTONDOWN:\r
4708   case WM_LBUTTONUP:\r
4709   case WM_MBUTTONUP:\r
4710   case WM_RBUTTONUP:\r
4711   case WM_MOUSEMOVE:\r
4712   case WM_MOUSEWHEEL:\r
4713     MouseEvent(hwnd, message, wParam, lParam);\r
4714     break;\r
4715 \r
4716   case WM_KEYUP:\r
4717     if((char)wParam == '\b') {\r
4718       ForwardEvent(); peek = 0;\r
4719     }\r
4720 \r
4721     JAWS_KBUP_NAVIGATION\r
4722 \r
4723     break;\r
4724 \r
4725   case WM_KEYDOWN:\r
4726     if((char)wParam == '\b') {\r
4727       if(!peek) BackwardEvent(), peek = 1;\r
4728     }\r
4729 \r
4730     JAWS_KBDOWN_NAVIGATION\r
4731 \r
4732     break;\r
4733 \r
4734   case WM_CHAR:\r
4735     \r
4736     JAWS_ALT_INTERCEPT\r
4737 \r
4738     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4739         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4740         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4741         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4742         SetFocus(h);\r
4743         SendMessage(h, message, wParam, lParam);\r
4744     } else if(lParam != KF_REPEAT) {\r
4745         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4746                 TypeInEvent((char)wParam);\r
4747         } else if((char)wParam == 003) CopyGameToClipboard();\r
4748          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4749     }\r
4750 \r
4751     break;\r
4752 \r
4753   case WM_PALETTECHANGED:\r
4754     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4755       int nnew;\r
4756       HDC hdc = GetDC(hwndMain);\r
4757       SelectPalette(hdc, hPal, TRUE);\r
4758       nnew = RealizePalette(hdc);\r
4759       if (nnew > 0) {\r
4760         paletteChanged = TRUE;\r
4761 \r
4762         InvalidateRect(hwnd, &boardRect, FALSE);\r
4763       }\r
4764       ReleaseDC(hwnd, hdc);\r
4765     }\r
4766     break;\r
4767 \r
4768   case WM_QUERYNEWPALETTE:\r
4769     if (!appData.monoMode /*&& paletteChanged*/) {\r
4770       int nnew;\r
4771       HDC hdc = GetDC(hwndMain);\r
4772       paletteChanged = FALSE;\r
4773       SelectPalette(hdc, hPal, FALSE);\r
4774       nnew = RealizePalette(hdc);\r
4775       if (nnew > 0) {\r
4776         InvalidateRect(hwnd, &boardRect, FALSE);\r
4777       }\r
4778       ReleaseDC(hwnd, hdc);\r
4779       return TRUE;\r
4780     }\r
4781     return FALSE;\r
4782 \r
4783   case WM_COMMAND: /* message: command from application menu */\r
4784     wmId    = LOWORD(wParam);\r
4785     wmEvent = HIWORD(wParam);\r
4786 \r
4787     switch (wmId) {\r
4788     case IDM_NewGame:\r
4789       ResetGameEvent();\r
4790       SAY("new game enter a move to play against the computer with white");\r
4791       break;\r
4792 \r
4793     case IDM_NewGameFRC:\r
4794       if( NewGameFRC() == 0 ) {\r
4795         ResetGameEvent();\r
4796       }\r
4797       break;\r
4798 \r
4799     case IDM_NewVariant:\r
4800       NewVariantPopup(hwnd);\r
4801       break;\r
4802 \r
4803     case IDM_LoadGame:\r
4804       LoadGameDialog(hwnd, _("Load Game from File"));\r
4805       break;\r
4806 \r
4807     case IDM_LoadNextGame:\r
4808       ReloadGame(1);\r
4809       break;\r
4810 \r
4811     case IDM_LoadPrevGame:\r
4812       ReloadGame(-1);\r
4813       break;\r
4814 \r
4815     case IDM_ReloadGame:\r
4816       ReloadGame(0);\r
4817       break;\r
4818 \r
4819     case IDM_LoadPosition:\r
4820       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4821         Reset(FALSE, TRUE);\r
4822       }\r
4823       number = 1;\r
4824       f = OpenFileDialog(hwnd, "rb", "",\r
4825                          appData.oldSaveStyle ? "pos" : "fen",\r
4826                          POSITION_FILT,\r
4827                          _("Load Position from File"), &number, fileTitle, NULL);\r
4828       if (f != NULL) {\r
4829         LoadPosition(f, number, fileTitle);\r
4830       }\r
4831       break;\r
4832 \r
4833     case IDM_LoadNextPosition:\r
4834       ReloadPosition(1);\r
4835       break;\r
4836 \r
4837     case IDM_LoadPrevPosition:\r
4838       ReloadPosition(-1);\r
4839       break;\r
4840 \r
4841     case IDM_ReloadPosition:\r
4842       ReloadPosition(0);\r
4843       break;\r
4844 \r
4845     case IDM_SaveGame:\r
4846       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4847       f = OpenFileDialog(hwnd, "a", defName,\r
4848                          appData.oldSaveStyle ? "gam" : "pgn",\r
4849                          GAME_FILT,\r
4850                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4851       if (f != NULL) {\r
4852         SaveGame(f, 0, "");\r
4853       }\r
4854       break;\r
4855 \r
4856     case IDM_SavePosition:\r
4857       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4858       f = OpenFileDialog(hwnd, "a", defName,\r
4859                          appData.oldSaveStyle ? "pos" : "fen",\r
4860                          POSITION_FILT,\r
4861                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4862       if (f != NULL) {\r
4863         SavePosition(f, 0, "");\r
4864       }\r
4865       break;\r
4866 \r
4867     case IDM_SaveDiagram:\r
4868       defName = "diagram";\r
4869       f = OpenFileDialog(hwnd, "wb", defName,\r
4870                          "bmp",\r
4871                          DIAGRAM_FILT,\r
4872                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4873       if (f != NULL) {\r
4874         SaveDiagram(f);\r
4875       }\r
4876       break;\r
4877 \r
4878     case IDM_CreateBook:\r
4879       CreateBookEvent();\r
4880       break;\r
4881 \r
4882     case IDM_CopyGame:\r
4883       CopyGameToClipboard();\r
4884       break;\r
4885 \r
4886     case IDM_PasteGame:\r
4887       PasteGameFromClipboard();\r
4888       break;\r
4889 \r
4890     case IDM_CopyGameListToClipboard:\r
4891       CopyGameListToClipboard();\r
4892       break;\r
4893 \r
4894     /* [AS] Autodetect FEN or PGN data */\r
4895     case IDM_PasteAny:\r
4896       PasteGameOrFENFromClipboard();\r
4897       break;\r
4898 \r
4899     /* [AS] Move history */\r
4900     case IDM_ShowMoveHistory:\r
4901         if( MoveHistoryIsUp() ) {\r
4902             MoveHistoryPopDown();\r
4903         }\r
4904         else {\r
4905             MoveHistoryPopUp();\r
4906         }\r
4907         break;\r
4908 \r
4909     /* [AS] Eval graph */\r
4910     case IDM_ShowEvalGraph:\r
4911         if( EvalGraphIsUp() ) {\r
4912             EvalGraphPopDown();\r
4913         }\r
4914         else {\r
4915             EvalGraphPopUp();\r
4916             SetFocus(hwndMain);\r
4917         }\r
4918         break;\r
4919 \r
4920     /* [AS] Engine output */\r
4921     case IDM_ShowEngineOutput:\r
4922         if( EngineOutputIsUp() ) {\r
4923             EngineOutputPopDown();\r
4924         }\r
4925         else {\r
4926             EngineOutputPopUp();\r
4927         }\r
4928         break;\r
4929 \r
4930     /* [AS] User adjudication */\r
4931     case IDM_UserAdjudication_White:\r
4932         UserAdjudicationEvent( +1 );\r
4933         break;\r
4934 \r
4935     case IDM_UserAdjudication_Black:\r
4936         UserAdjudicationEvent( -1 );\r
4937         break;\r
4938 \r
4939     case IDM_UserAdjudication_Draw:\r
4940         UserAdjudicationEvent( 0 );\r
4941         break;\r
4942 \r
4943     /* [AS] Game list options dialog */\r
4944     case IDM_GameListOptions:\r
4945       GameListOptions();\r
4946       break;\r
4947 \r
4948     case IDM_NewChat:\r
4949       ChatPopUp(NULL);\r
4950       break;\r
4951 \r
4952     case IDM_CopyPosition:\r
4953       CopyFENToClipboard();\r
4954       break;\r
4955 \r
4956     case IDM_PastePosition:\r
4957       PasteFENFromClipboard();\r
4958       break;\r
4959 \r
4960     case IDM_MailMove:\r
4961       MailMoveEvent();\r
4962       break;\r
4963 \r
4964     case IDM_ReloadCMailMsg:\r
4965       Reset(TRUE, TRUE);\r
4966       ReloadCmailMsgEvent(FALSE);\r
4967       break;\r
4968 \r
4969     case IDM_Minimize:\r
4970       ShowWindow(hwnd, SW_MINIMIZE);\r
4971       break;\r
4972 \r
4973     case IDM_Exit:\r
4974       ExitEvent(0);\r
4975       break;\r
4976 \r
4977     case IDM_MachineWhite:\r
4978       MachineWhiteEvent();\r
4979       /*\r
4980        * refresh the tags dialog only if it's visible\r
4981        */\r
4982       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4983           char *tags;\r
4984           tags = PGNTags(&gameInfo);\r
4985           TagsPopUp(tags, CmailMsg());\r
4986           free(tags);\r
4987       }\r
4988       SAY("computer starts playing white");\r
4989       break;\r
4990 \r
4991     case IDM_MachineBlack:\r
4992       MachineBlackEvent();\r
4993       /*\r
4994        * refresh the tags dialog only if it's visible\r
4995        */\r
4996       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4997           char *tags;\r
4998           tags = PGNTags(&gameInfo);\r
4999           TagsPopUp(tags, CmailMsg());\r
5000           free(tags);\r
5001       }\r
5002       SAY("computer starts playing black");\r
5003       break;\r
5004 \r
5005     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5006       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5007       break;\r
5008 \r
5009     case IDM_TwoMachines:\r
5010       TwoMachinesEvent();\r
5011       /*\r
5012        * refresh the tags dialog only if it's visible\r
5013        */\r
5014       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5015           char *tags;\r
5016           tags = PGNTags(&gameInfo);\r
5017           TagsPopUp(tags, CmailMsg());\r
5018           free(tags);\r
5019       }\r
5020       SAY("computer starts playing both sides");\r
5021       break;\r
5022 \r
5023     case IDM_AnalysisMode:\r
5024       if(AnalyzeModeEvent()) {\r
5025         SAY("analyzing current position");\r
5026       }\r
5027       break;\r
5028 \r
5029     case IDM_AnalyzeFile:\r
5030       AnalyzeFileEvent();\r
5031       break;\r
5032 \r
5033     case IDM_IcsClient:\r
5034       IcsClientEvent();\r
5035       break;\r
5036 \r
5037     case IDM_EditGame:\r
5038     case IDM_EditGame2:\r
5039       EditGameEvent();\r
5040       SAY("edit game");\r
5041       break;\r
5042 \r
5043     case IDM_EditPosition:\r
5044     case IDM_EditPosition2:\r
5045       EditPositionEvent();\r
5046       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5047       break;\r
5048 \r
5049     case IDM_Training:\r
5050       TrainingEvent();\r
5051       break;\r
5052 \r
5053     case IDM_ShowGameList:\r
5054       ShowGameListProc();\r
5055       break;\r
5056 \r
5057     case IDM_EditProgs1:\r
5058       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5059       break;\r
5060 \r
5061     case IDM_LoadProg1:\r
5062      LoadEnginePopUp(hwndMain, 0);\r
5063       break;\r
5064 \r
5065     case IDM_LoadProg2:\r
5066      LoadEnginePopUp(hwndMain, 1);\r
5067       break;\r
5068 \r
5069     case IDM_EditServers:\r
5070       EditTagsPopUp(icsNames, &icsNames);\r
5071       break;\r
5072 \r
5073     case IDM_EditTags:\r
5074     case IDM_Tags:\r
5075       EditTagsProc();\r
5076       break;\r
5077 \r
5078     case IDM_EditBook:\r
5079       EditBookEvent();\r
5080       break;\r
5081 \r
5082     case IDM_EditComment:\r
5083     case IDM_Comment:\r
5084       if (commentUp && editComment) {\r
5085         CommentPopDown();\r
5086       } else {\r
5087         EditCommentEvent();\r
5088       }\r
5089       break;\r
5090 \r
5091     case IDM_Pause:\r
5092       PauseEvent();\r
5093       break;\r
5094 \r
5095     case IDM_Accept:\r
5096       AcceptEvent();\r
5097       break;\r
5098 \r
5099     case IDM_Decline:\r
5100       DeclineEvent();\r
5101       break;\r
5102 \r
5103     case IDM_Rematch:\r
5104       RematchEvent();\r
5105       break;\r
5106 \r
5107     case IDM_CallFlag:\r
5108       CallFlagEvent();\r
5109       break;\r
5110 \r
5111     case IDM_Draw:\r
5112       DrawEvent();\r
5113       break;\r
5114 \r
5115     case IDM_Adjourn:\r
5116       AdjournEvent();\r
5117       break;\r
5118 \r
5119     case IDM_Abort:\r
5120       AbortEvent();\r
5121       break;\r
5122 \r
5123     case IDM_Resign:\r
5124       ResignEvent();\r
5125       break;\r
5126 \r
5127     case IDM_StopObserving:\r
5128       StopObservingEvent();\r
5129       break;\r
5130 \r
5131     case IDM_StopExamining:\r
5132       StopExaminingEvent();\r
5133       break;\r
5134 \r
5135     case IDM_Upload:\r
5136       UploadGameEvent();\r
5137       break;\r
5138 \r
5139     case IDM_TypeInMove:\r
5140       TypeInEvent('\000');\r
5141       break;\r
5142 \r
5143     case IDM_TypeInName:\r
5144       PopUpNameDialog('\000');\r
5145       break;\r
5146 \r
5147     case IDM_Backward:\r
5148       BackwardEvent();\r
5149       SetFocus(hwndMain);\r
5150       break;\r
5151 \r
5152     JAWS_MENU_ITEMS\r
5153 \r
5154     case IDM_Forward:\r
5155       ForwardEvent();\r
5156       SetFocus(hwndMain);\r
5157       break;\r
5158 \r
5159     case IDM_ToStart:\r
5160       ToStartEvent();\r
5161       SetFocus(hwndMain);\r
5162       break;\r
5163 \r
5164     case IDM_ToEnd:\r
5165       ToEndEvent();\r
5166       SetFocus(hwndMain);\r
5167       break;\r
5168 \r
5169     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5170     case OPT_GameListPrev:\r
5171       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5172       break;\r
5173 \r
5174     case IDM_Revert:\r
5175       RevertEvent(FALSE);\r
5176       break;\r
5177 \r
5178     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5179       RevertEvent(TRUE);\r
5180       break;\r
5181 \r
5182     case IDM_TruncateGame:\r
5183       TruncateGameEvent();\r
5184       break;\r
5185 \r
5186     case IDM_MoveNow:\r
5187       MoveNowEvent();\r
5188       break;\r
5189 \r
5190     case IDM_RetractMove:\r
5191       RetractMoveEvent();\r
5192       break;\r
5193 \r
5194     case IDM_FlipView:\r
5195       flipView = !flipView;\r
5196       DrawPosition(FALSE, NULL);\r
5197       break;\r
5198 \r
5199     case IDM_FlipClock:\r
5200       flipClock = !flipClock;\r
5201       DisplayBothClocks();\r
5202       DisplayLogos();\r
5203       break;\r
5204 \r
5205     case IDM_MuteSounds:\r
5206       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5207       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5208                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5209       break;\r
5210 \r
5211     case IDM_GeneralOptions:\r
5212       GeneralOptionsPopup(hwnd);\r
5213       DrawPosition(TRUE, NULL);\r
5214       break;\r
5215 \r
5216     case IDM_BoardOptions:\r
5217       BoardOptionsPopup(hwnd);\r
5218       break;\r
5219 \r
5220     case IDM_ThemeOptions:\r
5221       ThemeOptionsPopup(hwnd);\r
5222       break;\r
5223 \r
5224     case IDM_EnginePlayOptions:\r
5225       EnginePlayOptionsPopup(hwnd);\r
5226       break;\r
5227 \r
5228     case IDM_Engine1Options:\r
5229       EngineOptionsPopup(hwnd, &first);\r
5230       break;\r
5231 \r
5232     case IDM_Engine2Options:\r
5233       savedHwnd = hwnd;\r
5234       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5235       EngineOptionsPopup(hwnd, &second);\r
5236       break;\r
5237 \r
5238     case IDM_OptionsUCI:\r
5239       UciOptionsPopup(hwnd);\r
5240       break;\r
5241 \r
5242     case IDM_Tourney:\r
5243       TourneyPopup(hwnd);\r
5244       break;\r
5245 \r
5246     case IDM_IcsOptions:\r
5247       IcsOptionsPopup(hwnd);\r
5248       break;\r
5249 \r
5250     case IDM_Fonts:\r
5251       FontsOptionsPopup(hwnd);\r
5252       break;\r
5253 \r
5254     case IDM_Sounds:\r
5255       SoundOptionsPopup(hwnd);\r
5256       break;\r
5257 \r
5258     case IDM_CommPort:\r
5259       CommPortOptionsPopup(hwnd);\r
5260       break;\r
5261 \r
5262     case IDM_LoadOptions:\r
5263       LoadOptionsPopup(hwnd);\r
5264       break;\r
5265 \r
5266     case IDM_SaveOptions:\r
5267       SaveOptionsPopup(hwnd);\r
5268       break;\r
5269 \r
5270     case IDM_TimeControl:\r
5271       TimeControlOptionsPopup(hwnd);\r
5272       break;\r
5273 \r
5274     case IDM_SaveSettings:\r
5275       SaveSettings(settingsFileName);\r
5276       break;\r
5277 \r
5278     case IDM_SaveSettingsOnExit:\r
5279       saveSettingsOnExit = !saveSettingsOnExit;\r
5280       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5281                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5282                                          MF_CHECKED : MF_UNCHECKED));\r
5283       break;\r
5284 \r
5285     case IDM_Hint:\r
5286       HintEvent();\r
5287       break;\r
5288 \r
5289     case IDM_Book:\r
5290       BookEvent();\r
5291       break;\r
5292 \r
5293     case IDM_AboutGame:\r
5294       AboutGameEvent();\r
5295       break;\r
5296 \r
5297     case IDM_Debug:\r
5298       appData.debugMode = !appData.debugMode;\r
5299       if (appData.debugMode) {\r
5300         char dir[MSG_SIZ];\r
5301         GetCurrentDirectory(MSG_SIZ, dir);\r
5302         SetCurrentDirectory(installDir);\r
5303         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5304         SetCurrentDirectory(dir);\r
5305         setbuf(debugFP, NULL);\r
5306       } else {\r
5307         fclose(debugFP);\r
5308         debugFP = NULL;\r
5309       }\r
5310       break;\r
5311 \r
5312     case IDM_HELPCONTENTS:\r
5313       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5314           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5315           MessageBox (GetFocus(),\r
5316                     _("Unable to activate help"),\r
5317                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5318       }\r
5319       break;\r
5320 \r
5321     case IDM_HELPSEARCH:\r
5322         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5323             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5324         MessageBox (GetFocus(),\r
5325                     _("Unable to activate help"),\r
5326                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5327       }\r
5328       break;\r
5329 \r
5330     case IDM_HELPHELP:\r
5331       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5332         MessageBox (GetFocus(),\r
5333                     _("Unable to activate help"),\r
5334                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5335       }\r
5336       break;\r
5337 \r
5338     case IDM_ABOUT:\r
5339       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5340       DialogBox(hInst, \r
5341         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5342         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5343       FreeProcInstance(lpProc);\r
5344       break;\r
5345 \r
5346     case IDM_DirectCommand1:\r
5347       AskQuestionEvent(_("Direct Command"),\r
5348                        _("Send to chess program:"), "", "1");\r
5349       break;\r
5350     case IDM_DirectCommand2:\r
5351       AskQuestionEvent(_("Direct Command"),\r
5352                        _("Send to second chess program:"), "", "2");\r
5353       break;\r
5354 \r
5355     case EP_WhitePawn:\r
5356       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5357       fromX = fromY = -1;\r
5358       break;\r
5359 \r
5360     case EP_WhiteKnight:\r
5361       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5362       fromX = fromY = -1;\r
5363       break;\r
5364 \r
5365     case EP_WhiteBishop:\r
5366       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5367       fromX = fromY = -1;\r
5368       break;\r
5369 \r
5370     case EP_WhiteRook:\r
5371       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5372       fromX = fromY = -1;\r
5373       break;\r
5374 \r
5375     case EP_WhiteQueen:\r
5376       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5377       fromX = fromY = -1;\r
5378       break;\r
5379 \r
5380     case EP_WhiteFerz:\r
5381       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5382       fromX = fromY = -1;\r
5383       break;\r
5384 \r
5385     case EP_WhiteWazir:\r
5386       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5387       fromX = fromY = -1;\r
5388       break;\r
5389 \r
5390     case EP_WhiteAlfil:\r
5391       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5392       fromX = fromY = -1;\r
5393       break;\r
5394 \r
5395     case EP_WhiteCannon:\r
5396       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5397       fromX = fromY = -1;\r
5398       break;\r
5399 \r
5400     case EP_WhiteCardinal:\r
5401       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5402       fromX = fromY = -1;\r
5403       break;\r
5404 \r
5405     case EP_WhiteMarshall:\r
5406       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5407       fromX = fromY = -1;\r
5408       break;\r
5409 \r
5410     case EP_WhiteKing:\r
5411       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5412       fromX = fromY = -1;\r
5413       break;\r
5414 \r
5415     case EP_BlackPawn:\r
5416       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5417       fromX = fromY = -1;\r
5418       break;\r
5419 \r
5420     case EP_BlackKnight:\r
5421       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5422       fromX = fromY = -1;\r
5423       break;\r
5424 \r
5425     case EP_BlackBishop:\r
5426       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5427       fromX = fromY = -1;\r
5428       break;\r
5429 \r
5430     case EP_BlackRook:\r
5431       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5432       fromX = fromY = -1;\r
5433       break;\r
5434 \r
5435     case EP_BlackQueen:\r
5436       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5437       fromX = fromY = -1;\r
5438       break;\r
5439 \r
5440     case EP_BlackFerz:\r
5441       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5442       fromX = fromY = -1;\r
5443       break;\r
5444 \r
5445     case EP_BlackWazir:\r
5446       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5447       fromX = fromY = -1;\r
5448       break;\r
5449 \r
5450     case EP_BlackAlfil:\r
5451       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5452       fromX = fromY = -1;\r
5453       break;\r
5454 \r
5455     case EP_BlackCannon:\r
5456       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5457       fromX = fromY = -1;\r
5458       break;\r
5459 \r
5460     case EP_BlackCardinal:\r
5461       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5462       fromX = fromY = -1;\r
5463       break;\r
5464 \r
5465     case EP_BlackMarshall:\r
5466       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5467       fromX = fromY = -1;\r
5468       break;\r
5469 \r
5470     case EP_BlackKing:\r
5471       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5472       fromX = fromY = -1;\r
5473       break;\r
5474 \r
5475     case EP_EmptySquare:\r
5476       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5477       fromX = fromY = -1;\r
5478       break;\r
5479 \r
5480     case EP_ClearBoard:\r
5481       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5482       fromX = fromY = -1;\r
5483       break;\r
5484 \r
5485     case EP_White:\r
5486       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5487       fromX = fromY = -1;\r
5488       break;\r
5489 \r
5490     case EP_Black:\r
5491       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5492       fromX = fromY = -1;\r
5493       break;\r
5494 \r
5495     case EP_Promote:\r
5496       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5497       fromX = fromY = -1;\r
5498       break;\r
5499 \r
5500     case EP_Demote:\r
5501       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5502       fromX = fromY = -1;\r
5503       break;\r
5504 \r
5505     case DP_Pawn:\r
5506       DropMenuEvent(WhitePawn, fromX, fromY);\r
5507       fromX = fromY = -1;\r
5508       break;\r
5509 \r
5510     case DP_Knight:\r
5511       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5512       fromX = fromY = -1;\r
5513       break;\r
5514 \r
5515     case DP_Bishop:\r
5516       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5517       fromX = fromY = -1;\r
5518       break;\r
5519 \r
5520     case DP_Rook:\r
5521       DropMenuEvent(WhiteRook, fromX, fromY);\r
5522       fromX = fromY = -1;\r
5523       break;\r
5524 \r
5525     case DP_Queen:\r
5526       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5527       fromX = fromY = -1;\r
5528       break;\r
5529 \r
5530     case IDM_English:\r
5531       barbaric = 0; appData.language = "";\r
5532       TranslateMenus(0);\r
5533       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5534       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5535       lastChecked = wmId;\r
5536       break;\r
5537 \r
5538     default:\r
5539       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5540           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5541       else\r
5542       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5543           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5544           TranslateMenus(0);\r
5545           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5546           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5547           lastChecked = wmId;\r
5548           break;\r
5549       }\r
5550       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5551     }\r
5552     break;\r
5553 \r
5554   case WM_TIMER:\r
5555     switch (wParam) {\r
5556     case CLOCK_TIMER_ID:\r
5557       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5558       clockTimerEvent = 0;\r
5559       DecrementClocks(); /* call into back end */\r
5560       break;\r
5561     case LOAD_GAME_TIMER_ID:\r
5562       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5563       loadGameTimerEvent = 0;\r
5564       AutoPlayGameLoop(); /* call into back end */\r
5565       break;\r
5566     case ANALYSIS_TIMER_ID:\r
5567       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5568                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5569         AnalysisPeriodicEvent(0);\r
5570       } else {\r
5571         KillTimer(hwnd, analysisTimerEvent);\r
5572         analysisTimerEvent = 0;\r
5573       }\r
5574       break;\r
5575     case DELAYED_TIMER_ID:\r
5576       KillTimer(hwnd, delayedTimerEvent);\r
5577       delayedTimerEvent = 0;\r
5578       delayedTimerCallback();\r
5579       break;\r
5580     }\r
5581     break;\r
5582 \r
5583   case WM_USER_Input:\r
5584     InputEvent(hwnd, message, wParam, lParam);\r
5585     break;\r
5586 \r
5587   /* [AS] Also move "attached" child windows */\r
5588   case WM_WINDOWPOSCHANGING:\r
5589 \r
5590     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5591         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5592 \r
5593         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5594             /* Window is moving */\r
5595             RECT rcMain;\r
5596 \r
5597 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5598             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5599             rcMain.right  = wpMain.x + wpMain.width;\r
5600             rcMain.top    = wpMain.y;\r
5601             rcMain.bottom = wpMain.y + wpMain.height;\r
5602             \r
5603             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5604             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5605             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5606             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5607             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5608             wpMain.x = lpwp->x;\r
5609             wpMain.y = lpwp->y;\r
5610         }\r
5611     }\r
5612     break;\r
5613 \r
5614   /* [AS] Snapping */\r
5615   case WM_ENTERSIZEMOVE:\r
5616     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5617     if (hwnd == hwndMain) {\r
5618       doingSizing = TRUE;\r
5619       lastSizing = 0;\r
5620     }\r
5621     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5622     break;\r
5623 \r
5624   case WM_SIZING:\r
5625     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5626     if (hwnd == hwndMain) {\r
5627       lastSizing = wParam;\r
5628     }\r
5629     break;\r
5630 \r
5631   case WM_MOVING:\r
5632     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5633       return OnMoving( &sd, hwnd, wParam, lParam );\r
5634 \r
5635   case WM_EXITSIZEMOVE:\r
5636     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5637     if (hwnd == hwndMain) {\r
5638       RECT client;\r
5639       doingSizing = FALSE;\r
5640       InvalidateRect(hwnd, &boardRect, FALSE);\r
5641       GetClientRect(hwnd, &client);\r
5642       ResizeBoard(client.right, client.bottom, lastSizing);\r
5643       lastSizing = 0;\r
5644       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5645     }\r
5646     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5647     break;\r
5648 \r
5649   case WM_DESTROY: /* message: window being destroyed */\r
5650     PostQuitMessage(0);\r
5651     break;\r
5652 \r
5653   case WM_CLOSE:\r
5654     if (hwnd == hwndMain) {\r
5655       ExitEvent(0);\r
5656     }\r
5657     break;\r
5658 \r
5659   default:      /* Passes it on if unprocessed */\r
5660     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5661   }\r
5662   return 0;\r
5663 }\r
5664 \r
5665 /*---------------------------------------------------------------------------*\\r
5666  *\r
5667  * Misc utility routines\r
5668  *\r
5669 \*---------------------------------------------------------------------------*/\r
5670 \r
5671 /*\r
5672  * Decent random number generator, at least not as bad as Windows\r
5673  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5674  */\r
5675 unsigned int randstate;\r
5676 \r
5677 int\r
5678 myrandom(void)\r
5679 {\r
5680   randstate = randstate * 1664525 + 1013904223;\r
5681   return (int) randstate & 0x7fffffff;\r
5682 }\r
5683 \r
5684 void\r
5685 mysrandom(unsigned int seed)\r
5686 {\r
5687   randstate = seed;\r
5688 }\r
5689 \r
5690 \r
5691 /* \r
5692  * returns TRUE if user selects a different color, FALSE otherwise \r
5693  */\r
5694 \r
5695 BOOL\r
5696 ChangeColor(HWND hwnd, COLORREF *which)\r
5697 {\r
5698   static BOOL firstTime = TRUE;\r
5699   static DWORD customColors[16];\r
5700   CHOOSECOLOR cc;\r
5701   COLORREF newcolor;\r
5702   int i;\r
5703   ColorClass ccl;\r
5704 \r
5705   if (firstTime) {\r
5706     /* Make initial colors in use available as custom colors */\r
5707     /* Should we put the compiled-in defaults here instead? */\r
5708     i = 0;\r
5709     customColors[i++] = lightSquareColor & 0xffffff;\r
5710     customColors[i++] = darkSquareColor & 0xffffff;\r
5711     customColors[i++] = whitePieceColor & 0xffffff;\r
5712     customColors[i++] = blackPieceColor & 0xffffff;\r
5713     customColors[i++] = highlightSquareColor & 0xffffff;\r
5714     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5715 \r
5716     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5717       customColors[i++] = textAttribs[ccl].color;\r
5718     }\r
5719     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5720     firstTime = FALSE;\r
5721   }\r
5722 \r
5723   cc.lStructSize = sizeof(cc);\r
5724   cc.hwndOwner = hwnd;\r
5725   cc.hInstance = NULL;\r
5726   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5727   cc.lpCustColors = (LPDWORD) customColors;\r
5728   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5729 \r
5730   if (!ChooseColor(&cc)) return FALSE;\r
5731 \r
5732   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5733   if (newcolor == *which) return FALSE;\r
5734   *which = newcolor;\r
5735   return TRUE;\r
5736 \r
5737   /*\r
5738   InitDrawingColors();\r
5739   InvalidateRect(hwnd, &boardRect, FALSE);\r
5740   */\r
5741 }\r
5742 \r
5743 BOOLEAN\r
5744 MyLoadSound(MySound *ms)\r
5745 {\r
5746   BOOL ok = FALSE;\r
5747   struct stat st;\r
5748   FILE *f;\r
5749 \r
5750   if (ms->data && ms->flag) free(ms->data);\r
5751   ms->data = NULL;\r
5752 \r
5753   switch (ms->name[0]) {\r
5754   case NULLCHAR:\r
5755     /* Silence */\r
5756     ok = TRUE;\r
5757     break;\r
5758   case '$':\r
5759     /* System sound from Control Panel.  Don't preload here. */\r
5760     ok = TRUE;\r
5761     break;\r
5762   case '!':\r
5763     if (ms->name[1] == NULLCHAR) {\r
5764       /* "!" alone = silence */\r
5765       ok = TRUE;\r
5766     } else {\r
5767       /* Builtin wave resource.  Error if not found. */\r
5768       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5769       if (h == NULL) break;\r
5770       ms->data = (void *)LoadResource(hInst, h);\r
5771       ms->flag = 0; // not maloced, so cannot be freed!\r
5772       if (h == NULL) break;\r
5773       ok = TRUE;\r
5774     }\r
5775     break;\r
5776   default:\r
5777     /* .wav file.  Error if not found. */\r
5778     f = fopen(ms->name, "rb");\r
5779     if (f == NULL) break;\r
5780     if (fstat(fileno(f), &st) < 0) break;\r
5781     ms->data = malloc(st.st_size);\r
5782     ms->flag = 1;\r
5783     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5784     fclose(f);\r
5785     ok = TRUE;\r
5786     break;\r
5787   }\r
5788   if (!ok) {\r
5789     char buf[MSG_SIZ];\r
5790       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5791     DisplayError(buf, GetLastError());\r
5792   }\r
5793   return ok;\r
5794 }\r
5795 \r
5796 BOOLEAN\r
5797 MyPlaySound(MySound *ms)\r
5798 {\r
5799   BOOLEAN ok = FALSE;\r
5800 \r
5801   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5802   switch (ms->name[0]) {\r
5803   case NULLCHAR:\r
5804         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5805     /* Silence */\r
5806     ok = TRUE;\r
5807     break;\r
5808   case '$':\r
5809     /* System sound from Control Panel (deprecated feature).\r
5810        "$" alone or an unset sound name gets default beep (still in use). */\r
5811     if (ms->name[1]) {\r
5812       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5813     }\r
5814     if (!ok) ok = MessageBeep(MB_OK);\r
5815     break; \r
5816   case '!':\r
5817     /* Builtin wave resource, or "!" alone for silence */\r
5818     if (ms->name[1]) {\r
5819       if (ms->data == NULL) return FALSE;\r
5820       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5821     } else {\r
5822       ok = TRUE;\r
5823     }\r
5824     break;\r
5825   default:\r
5826     /* .wav file.  Error if not found. */\r
5827     if (ms->data == NULL) return FALSE;\r
5828     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5829     break;\r
5830   }\r
5831   /* Don't print an error: this can happen innocently if the sound driver\r
5832      is busy; for instance, if another instance of WinBoard is playing\r
5833      a sound at about the same time. */\r
5834   return ok;\r
5835 }\r
5836 \r
5837 \r
5838 LRESULT CALLBACK\r
5839 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5840 {\r
5841   BOOL ok;\r
5842   OPENFILENAME *ofn;\r
5843   static UINT *number; /* gross that this is static */\r
5844 \r
5845   switch (message) {\r
5846   case WM_INITDIALOG: /* message: initialize dialog box */\r
5847     /* Center the dialog over the application window */\r
5848     ofn = (OPENFILENAME *) lParam;\r
5849     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5850       number = (UINT *) ofn->lCustData;\r
5851       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5852     } else {\r
5853       number = NULL;\r
5854     }\r
5855     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5856     Translate(hDlg, 1536);\r
5857     return FALSE;  /* Allow for further processing */\r
5858 \r
5859   case WM_COMMAND:\r
5860     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5861       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5862     }\r
5863     return FALSE;  /* Allow for further processing */\r
5864   }\r
5865   return FALSE;\r
5866 }\r
5867 \r
5868 UINT APIENTRY\r
5869 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5870 {\r
5871   static UINT *number;\r
5872   OPENFILENAME *ofname;\r
5873   OFNOTIFY *ofnot;\r
5874   switch (uiMsg) {\r
5875   case WM_INITDIALOG:\r
5876     Translate(hdlg, DLG_IndexNumber);\r
5877     ofname = (OPENFILENAME *)lParam;\r
5878     number = (UINT *)(ofname->lCustData);\r
5879     break;\r
5880   case WM_NOTIFY:\r
5881     ofnot = (OFNOTIFY *)lParam;\r
5882     if (ofnot->hdr.code == CDN_FILEOK) {\r
5883       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5884     }\r
5885     break;\r
5886   }\r
5887   return 0;\r
5888 }\r
5889 \r
5890 \r
5891 FILE *\r
5892 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5893                char *nameFilt, char *dlgTitle, UINT *number,\r
5894                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5895 {\r
5896   OPENFILENAME openFileName;\r
5897   char buf1[MSG_SIZ];\r
5898   FILE *f;\r
5899 \r
5900   if (fileName == NULL) fileName = buf1;\r
5901   if (defName == NULL) {\r
5902     safeStrCpy(fileName, "*.", 3 );\r
5903     strcat(fileName, defExt);\r
5904   } else {\r
5905     safeStrCpy(fileName, defName, MSG_SIZ );\r
5906   }\r
5907     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5908   if (number) *number = 0;\r
5909 \r
5910   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5911   openFileName.hwndOwner         = hwnd;\r
5912   openFileName.hInstance         = (HANDLE) hInst;\r
5913   openFileName.lpstrFilter       = nameFilt;\r
5914   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5915   openFileName.nMaxCustFilter    = 0L;\r
5916   openFileName.nFilterIndex      = 1L;\r
5917   openFileName.lpstrFile         = fileName;\r
5918   openFileName.nMaxFile          = MSG_SIZ;\r
5919   openFileName.lpstrFileTitle    = fileTitle;\r
5920   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5921   openFileName.lpstrInitialDir   = NULL;\r
5922   openFileName.lpstrTitle        = dlgTitle;\r
5923   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5924     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5925     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5926     | (oldDialog ? 0 : OFN_EXPLORER);\r
5927   openFileName.nFileOffset       = 0;\r
5928   openFileName.nFileExtension    = 0;\r
5929   openFileName.lpstrDefExt       = defExt;\r
5930   openFileName.lCustData         = (LONG) number;\r
5931   openFileName.lpfnHook          = oldDialog ?\r
5932     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5933   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5934 \r
5935   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5936                         GetOpenFileName(&openFileName)) {\r
5937     /* open the file */\r
5938     f = fopen(openFileName.lpstrFile, write);\r
5939     if (f == NULL) {\r
5940       MessageBox(hwnd, _("File open failed"), NULL,\r
5941                  MB_OK|MB_ICONEXCLAMATION);\r
5942       return NULL;\r
5943     }\r
5944   } else {\r
5945     int err = CommDlgExtendedError();\r
5946     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5947     return FALSE;\r
5948   }\r
5949   return f;\r
5950 }\r
5951 \r
5952 \r
5953 \r
5954 VOID APIENTRY\r
5955 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5956 {\r
5957   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5958 \r
5959   /*\r
5960    * Get the first pop-up menu in the menu template. This is the\r
5961    * menu that TrackPopupMenu displays.\r
5962    */\r
5963   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5964   TranslateOneMenu(10, hmenuTrackPopup);\r
5965 \r
5966   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5967 \r
5968   /*\r
5969    * TrackPopup uses screen coordinates, so convert the\r
5970    * coordinates of the mouse click to screen coordinates.\r
5971    */\r
5972   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5973 \r
5974   /* Draw and track the floating pop-up menu. */\r
5975   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5976                  pt.x, pt.y, 0, hwnd, NULL);\r
5977 \r
5978   /* Destroy the menu.*/\r
5979   DestroyMenu(hmenu);\r
5980 }\r
5981    \r
5982 typedef struct {\r
5983   HWND hDlg, hText;\r
5984   int sizeX, sizeY, newSizeX, newSizeY;\r
5985   HDWP hdwp;\r
5986 } ResizeEditPlusButtonsClosure;\r
5987 \r
5988 BOOL CALLBACK\r
5989 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5990 {\r
5991   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5992   RECT rect;\r
5993   POINT pt;\r
5994 \r
5995   if (hChild == cl->hText) return TRUE;\r
5996   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5997   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5998   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5999   ScreenToClient(cl->hDlg, &pt);\r
6000   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6001     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6002   return TRUE;\r
6003 }\r
6004 \r
6005 /* Resize a dialog that has a (rich) edit field filling most of\r
6006    the top, with a row of buttons below */\r
6007 VOID\r
6008 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6009 {\r
6010   RECT rectText;\r
6011   int newTextHeight, newTextWidth;\r
6012   ResizeEditPlusButtonsClosure cl;\r
6013   \r
6014   /*if (IsIconic(hDlg)) return;*/\r
6015   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6016   \r
6017   cl.hdwp = BeginDeferWindowPos(8);\r
6018 \r
6019   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6020   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6021   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6022   if (newTextHeight < 0) {\r
6023     newSizeY += -newTextHeight;\r
6024     newTextHeight = 0;\r
6025   }\r
6026   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6027     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6028 \r
6029   cl.hDlg = hDlg;\r
6030   cl.hText = hText;\r
6031   cl.sizeX = sizeX;\r
6032   cl.sizeY = sizeY;\r
6033   cl.newSizeX = newSizeX;\r
6034   cl.newSizeY = newSizeY;\r
6035   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6036 \r
6037   EndDeferWindowPos(cl.hdwp);\r
6038 }\r
6039 \r
6040 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6041 {\r
6042     RECT    rChild, rParent;\r
6043     int     wChild, hChild, wParent, hParent;\r
6044     int     wScreen, hScreen, xNew, yNew;\r
6045     HDC     hdc;\r
6046 \r
6047     /* Get the Height and Width of the child window */\r
6048     GetWindowRect (hwndChild, &rChild);\r
6049     wChild = rChild.right - rChild.left;\r
6050     hChild = rChild.bottom - rChild.top;\r
6051 \r
6052     /* Get the Height and Width of the parent window */\r
6053     GetWindowRect (hwndParent, &rParent);\r
6054     wParent = rParent.right - rParent.left;\r
6055     hParent = rParent.bottom - rParent.top;\r
6056 \r
6057     /* Get the display limits */\r
6058     hdc = GetDC (hwndChild);\r
6059     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6060     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6061     ReleaseDC(hwndChild, hdc);\r
6062 \r
6063     /* Calculate new X position, then adjust for screen */\r
6064     xNew = rParent.left + ((wParent - wChild) /2);\r
6065     if (xNew < 0) {\r
6066         xNew = 0;\r
6067     } else if ((xNew+wChild) > wScreen) {\r
6068         xNew = wScreen - wChild;\r
6069     }\r
6070 \r
6071     /* Calculate new Y position, then adjust for screen */\r
6072     if( mode == 0 ) {\r
6073         yNew = rParent.top  + ((hParent - hChild) /2);\r
6074     }\r
6075     else {\r
6076         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6077     }\r
6078 \r
6079     if (yNew < 0) {\r
6080         yNew = 0;\r
6081     } else if ((yNew+hChild) > hScreen) {\r
6082         yNew = hScreen - hChild;\r
6083     }\r
6084 \r
6085     /* Set it, and return */\r
6086     return SetWindowPos (hwndChild, NULL,\r
6087                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6088 }\r
6089 \r
6090 /* Center one window over another */\r
6091 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6092 {\r
6093     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6094 }\r
6095 \r
6096 /*---------------------------------------------------------------------------*\\r
6097  *\r
6098  * Startup Dialog functions\r
6099  *\r
6100 \*---------------------------------------------------------------------------*/\r
6101 void\r
6102 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6103 {\r
6104   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6105 \r
6106   while (*cd != NULL) {\r
6107     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6108     cd++;\r
6109   }\r
6110 }\r
6111 \r
6112 void\r
6113 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6114 {\r
6115   char buf1[MAX_ARG_LEN];\r
6116   int len;\r
6117 \r
6118   if (str[0] == '@') {\r
6119     FILE* f = fopen(str + 1, "r");\r
6120     if (f == NULL) {\r
6121       DisplayFatalError(str + 1, errno, 2);\r
6122       return;\r
6123     }\r
6124     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6125     fclose(f);\r
6126     buf1[len] = NULLCHAR;\r
6127     str = buf1;\r
6128   }\r
6129 \r
6130   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6131 \r
6132   for (;;) {\r
6133     char buf[MSG_SIZ];\r
6134     char *end = strchr(str, '\n');\r
6135     if (end == NULL) return;\r
6136     memcpy(buf, str, end - str);\r
6137     buf[end - str] = NULLCHAR;\r
6138     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6139     str = end + 1;\r
6140   }\r
6141 }\r
6142 \r
6143 void\r
6144 SetStartupDialogEnables(HWND hDlg)\r
6145 {\r
6146   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6147     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6148     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6149   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6150     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6151   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6152     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6153   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6154     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6155   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6156     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6157     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6158     IsDlgButtonChecked(hDlg, OPT_View));\r
6159 }\r
6160 \r
6161 char *\r
6162 QuoteForFilename(char *filename)\r
6163 {\r
6164   int dquote, space;\r
6165   dquote = strchr(filename, '"') != NULL;\r
6166   space = strchr(filename, ' ') != NULL;\r
6167   if (dquote || space) {\r
6168     if (dquote) {\r
6169       return "'";\r
6170     } else {\r
6171       return "\"";\r
6172     }\r
6173   } else {\r
6174     return "";\r
6175   }\r
6176 }\r
6177 \r
6178 VOID\r
6179 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6180 {\r
6181   char buf[MSG_SIZ];\r
6182   char *q;\r
6183 \r
6184   InitComboStringsFromOption(hwndCombo, nthnames);\r
6185   q = QuoteForFilename(nthcp);\r
6186     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6187   if (*nthdir != NULLCHAR) {\r
6188     q = QuoteForFilename(nthdir);\r
6189       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6190   }\r
6191   if (*nthcp == NULLCHAR) {\r
6192     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6193   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6194     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6195     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6196   }\r
6197 }\r
6198 \r
6199 LRESULT CALLBACK\r
6200 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6201 {\r
6202   char buf[MSG_SIZ];\r
6203   HANDLE hwndCombo;\r
6204   char *p;\r
6205 \r
6206   switch (message) {\r
6207   case WM_INITDIALOG:\r
6208     /* Center the dialog */\r
6209     CenterWindow (hDlg, GetDesktopWindow());\r
6210     Translate(hDlg, DLG_Startup);\r
6211     /* Initialize the dialog items */\r
6212     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6213                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6214                   firstChessProgramNames);\r
6215     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6216                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6217                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6218     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6219     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6220       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6221     if (*appData.icsHelper != NULLCHAR) {\r
6222       char *q = QuoteForFilename(appData.icsHelper);\r
6223       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6224     }\r
6225     if (*appData.icsHost == NULLCHAR) {\r
6226       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6227       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6228     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6229       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6230       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6231     }\r
6232 \r
6233     if (appData.icsActive) {\r
6234       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6235     }\r
6236     else if (appData.noChessProgram) {\r
6237       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6238     }\r
6239     else {\r
6240       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6241     }\r
6242 \r
6243     SetStartupDialogEnables(hDlg);\r
6244     return TRUE;\r
6245 \r
6246   case WM_COMMAND:\r
6247     switch (LOWORD(wParam)) {\r
6248     case IDOK:\r
6249       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6250         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6251         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6252         p = buf;\r
6253         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6254         ParseArgs(StringGet, &p);\r
6255         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6256         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6257         p = buf;\r
6258         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6259         ParseArgs(StringGet, &p);\r
6260         SwapEngines(singleList); // ... and then make it 'second'\r
6261         appData.noChessProgram = FALSE;\r
6262         appData.icsActive = FALSE;\r
6263       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6264         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6265         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6266         p = buf;\r
6267         ParseArgs(StringGet, &p);\r
6268         if (appData.zippyPlay) {\r
6269           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6270           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6271           p = buf;\r
6272           ParseArgs(StringGet, &p);\r
6273         }\r
6274       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6275         appData.noChessProgram = TRUE;\r
6276         appData.icsActive = FALSE;\r
6277       } else {\r
6278         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6279                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6280         return TRUE;\r
6281       }\r
6282       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6283         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6284         p = buf;\r
6285         ParseArgs(StringGet, &p);\r
6286       }\r
6287       EndDialog(hDlg, TRUE);\r
6288       return TRUE;\r
6289 \r
6290     case IDCANCEL:\r
6291       ExitEvent(0);\r
6292       return TRUE;\r
6293 \r
6294     case IDM_HELPCONTENTS:\r
6295       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6296         MessageBox (GetFocus(),\r
6297                     _("Unable to activate help"),\r
6298                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6299       }\r
6300       break;\r
6301 \r
6302     default:\r
6303       SetStartupDialogEnables(hDlg);\r
6304       break;\r
6305     }\r
6306     break;\r
6307   }\r
6308   return FALSE;\r
6309 }\r
6310 \r
6311 /*---------------------------------------------------------------------------*\\r
6312  *\r
6313  * About box dialog functions\r
6314  *\r
6315 \*---------------------------------------------------------------------------*/\r
6316 \r
6317 /* Process messages for "About" dialog box */\r
6318 LRESULT CALLBACK\r
6319 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6320 {\r
6321   switch (message) {\r
6322   case WM_INITDIALOG: /* message: initialize dialog box */\r
6323     /* Center the dialog over the application window */\r
6324     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6325     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6326     Translate(hDlg, ABOUTBOX);\r
6327     JAWS_COPYRIGHT\r
6328     return (TRUE);\r
6329 \r
6330   case WM_COMMAND: /* message: received a command */\r
6331     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6332         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6333       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6334       return (TRUE);\r
6335     }\r
6336     break;\r
6337   }\r
6338   return (FALSE);\r
6339 }\r
6340 \r
6341 /*---------------------------------------------------------------------------*\\r
6342  *\r
6343  * Comment Dialog functions\r
6344  *\r
6345 \*---------------------------------------------------------------------------*/\r
6346 \r
6347 LRESULT CALLBACK\r
6348 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6349 {\r
6350   static HANDLE hwndText = NULL;\r
6351   int len, newSizeX, newSizeY, flags;\r
6352   static int sizeX, sizeY;\r
6353   char *str;\r
6354   RECT rect;\r
6355   MINMAXINFO *mmi;\r
6356 \r
6357   switch (message) {\r
6358   case WM_INITDIALOG: /* message: initialize dialog box */\r
6359     /* Initialize the dialog items */\r
6360     Translate(hDlg, DLG_EditComment);\r
6361     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6362     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6363     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6364     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6365     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6366     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6367     SetWindowText(hDlg, commentTitle);\r
6368     if (editComment) {\r
6369       SetFocus(hwndText);\r
6370     } else {\r
6371       SetFocus(GetDlgItem(hDlg, IDOK));\r
6372     }\r
6373     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6374                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6375                 MAKELPARAM(FALSE, 0));\r
6376     /* Size and position the dialog */\r
6377     if (!commentDialog) {\r
6378       commentDialog = hDlg;\r
6379       flags = SWP_NOZORDER;\r
6380       GetClientRect(hDlg, &rect);\r
6381       sizeX = rect.right;\r
6382       sizeY = rect.bottom;\r
6383       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6384           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6385         WINDOWPLACEMENT wp;\r
6386         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6387         wp.length = sizeof(WINDOWPLACEMENT);\r
6388         wp.flags = 0;\r
6389         wp.showCmd = SW_SHOW;\r
6390         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6391         wp.rcNormalPosition.left = wpComment.x;\r
6392         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6393         wp.rcNormalPosition.top = wpComment.y;\r
6394         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6395         SetWindowPlacement(hDlg, &wp);\r
6396 \r
6397         GetClientRect(hDlg, &rect);\r
6398         newSizeX = rect.right;\r
6399         newSizeY = rect.bottom;\r
6400         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6401                               newSizeX, newSizeY);\r
6402         sizeX = newSizeX;\r
6403         sizeY = newSizeY;\r
6404       }\r
6405     }\r
6406     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6407     return FALSE;\r
6408 \r
6409   case WM_COMMAND: /* message: received a command */\r
6410     switch (LOWORD(wParam)) {\r
6411     case IDOK:\r
6412       if (editComment) {\r
6413         char *p, *q;\r
6414         /* Read changed options from the dialog box */\r
6415         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6416         len = GetWindowTextLength(hwndText);\r
6417         str = (char *) malloc(len + 1);\r
6418         GetWindowText(hwndText, str, len + 1);\r
6419         p = q = str;\r
6420         while (*q) {\r
6421           if (*q == '\r')\r
6422             q++;\r
6423           else\r
6424             *p++ = *q++;\r
6425         }\r
6426         *p = NULLCHAR;\r
6427         ReplaceComment(commentIndex, str);\r
6428         free(str);\r
6429       }\r
6430       CommentPopDown();\r
6431       return TRUE;\r
6432 \r
6433     case IDCANCEL:\r
6434     case OPT_CancelComment:\r
6435       CommentPopDown();\r
6436       return TRUE;\r
6437 \r
6438     case OPT_ClearComment:\r
6439       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6440       break;\r
6441 \r
6442     case OPT_EditComment:\r
6443       EditCommentEvent();\r
6444       return TRUE;\r
6445 \r
6446     default:\r
6447       break;\r
6448     }\r
6449     break;\r
6450 \r
6451   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6452         if( wParam == OPT_CommentText ) {\r
6453             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6454 \r
6455             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6456                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6457                 POINTL pt;\r
6458                 LRESULT index;\r
6459 \r
6460                 pt.x = LOWORD( lpMF->lParam );\r
6461                 pt.y = HIWORD( lpMF->lParam );\r
6462 \r
6463                 if(lpMF->msg == WM_CHAR) {\r
6464                         CHARRANGE sel;\r
6465                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6466                         index = sel.cpMin;\r
6467                 } else\r
6468                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6469 \r
6470                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6471                 len = GetWindowTextLength(hwndText);\r
6472                 str = (char *) malloc(len + 1);\r
6473                 GetWindowText(hwndText, str, len + 1);\r
6474                 ReplaceComment(commentIndex, str);\r
6475                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6476                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6477                 free(str);\r
6478 \r
6479                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6480                 lpMF->msg = WM_USER;\r
6481 \r
6482                 return TRUE;\r
6483             }\r
6484         }\r
6485         break;\r
6486 \r
6487   case WM_SIZE:\r
6488     newSizeX = LOWORD(lParam);\r
6489     newSizeY = HIWORD(lParam);\r
6490     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6491     sizeX = newSizeX;\r
6492     sizeY = newSizeY;\r
6493     break;\r
6494 \r
6495   case WM_GETMINMAXINFO:\r
6496     /* Prevent resizing window too small */\r
6497     mmi = (MINMAXINFO *) lParam;\r
6498     mmi->ptMinTrackSize.x = 100;\r
6499     mmi->ptMinTrackSize.y = 100;\r
6500     break;\r
6501   }\r
6502   return FALSE;\r
6503 }\r
6504 \r
6505 VOID\r
6506 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6507 {\r
6508   FARPROC lpProc;\r
6509   char *p, *q;\r
6510 \r
6511   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6512 \r
6513   if (str == NULL) str = "";\r
6514   p = (char *) malloc(2 * strlen(str) + 2);\r
6515   q = p;\r
6516   while (*str) {\r
6517     if (*str == '\n') *q++ = '\r';\r
6518     *q++ = *str++;\r
6519   }\r
6520   *q = NULLCHAR;\r
6521   if (commentText != NULL) free(commentText);\r
6522 \r
6523   commentIndex = index;\r
6524   commentTitle = title;\r
6525   commentText = p;\r
6526   editComment = edit;\r
6527 \r
6528   if (commentDialog) {\r
6529     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6530     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6531   } else {\r
6532     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6533     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6534                  hwndMain, (DLGPROC)lpProc);\r
6535     FreeProcInstance(lpProc);\r
6536   }\r
6537   commentUp = TRUE;\r
6538 }\r
6539 \r
6540 \r
6541 /*---------------------------------------------------------------------------*\\r
6542  *\r
6543  * Type-in move dialog functions\r
6544  * \r
6545 \*---------------------------------------------------------------------------*/\r
6546 \r
6547 LRESULT CALLBACK\r
6548 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6549 {\r
6550   char move[MSG_SIZ];\r
6551   HWND hInput;\r
6552 \r
6553   switch (message) {\r
6554   case WM_INITDIALOG:\r
6555     move[0] = (char) lParam;\r
6556     move[1] = NULLCHAR;\r
6557     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6558     Translate(hDlg, DLG_TypeInMove);\r
6559     hInput = GetDlgItem(hDlg, OPT_Move);\r
6560     SetWindowText(hInput, move);\r
6561     SetFocus(hInput);\r
6562     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6563     return FALSE;\r
6564 \r
6565   case WM_COMMAND:\r
6566     switch (LOWORD(wParam)) {\r
6567     case IDOK:\r
6568 \r
6569       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6570       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6571       TypeInDoneEvent(move);\r
6572       EndDialog(hDlg, TRUE);\r
6573       return TRUE;\r
6574     case IDCANCEL:\r
6575       EndDialog(hDlg, FALSE);\r
6576       return TRUE;\r
6577     default:\r
6578       break;\r
6579     }\r
6580     break;\r
6581   }\r
6582   return FALSE;\r
6583 }\r
6584 \r
6585 VOID\r
6586 PopUpMoveDialog(char firstchar)\r
6587 {\r
6588     FARPROC lpProc;\r
6589 \r
6590       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6591       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6592         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6593       FreeProcInstance(lpProc);\r
6594 }\r
6595 \r
6596 /*---------------------------------------------------------------------------*\\r
6597  *\r
6598  * Type-in name dialog functions\r
6599  * \r
6600 \*---------------------------------------------------------------------------*/\r
6601 \r
6602 LRESULT CALLBACK\r
6603 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6604 {\r
6605   char move[MSG_SIZ];\r
6606   HWND hInput;\r
6607 \r
6608   switch (message) {\r
6609   case WM_INITDIALOG:\r
6610     move[0] = (char) lParam;\r
6611     move[1] = NULLCHAR;\r
6612     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6613     Translate(hDlg, DLG_TypeInName);\r
6614     hInput = GetDlgItem(hDlg, OPT_Name);\r
6615     SetWindowText(hInput, move);\r
6616     SetFocus(hInput);\r
6617     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6618     return FALSE;\r
6619 \r
6620   case WM_COMMAND:\r
6621     switch (LOWORD(wParam)) {\r
6622     case IDOK:\r
6623       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6624       appData.userName = strdup(move);\r
6625       SetUserLogo();\r
6626       SetGameInfo();\r
6627       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6628         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6629         DisplayTitle(move);\r
6630       }\r
6631 \r
6632 \r
6633       EndDialog(hDlg, TRUE);\r
6634       return TRUE;\r
6635     case IDCANCEL:\r
6636       EndDialog(hDlg, FALSE);\r
6637       return TRUE;\r
6638     default:\r
6639       break;\r
6640     }\r
6641     break;\r
6642   }\r
6643   return FALSE;\r
6644 }\r
6645 \r
6646 VOID\r
6647 PopUpNameDialog(char firstchar)\r
6648 {\r
6649     FARPROC lpProc;\r
6650     \r
6651       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6652       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6653         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6654       FreeProcInstance(lpProc);\r
6655 }\r
6656 \r
6657 /*---------------------------------------------------------------------------*\\r
6658  *\r
6659  *  Error dialogs\r
6660  * \r
6661 \*---------------------------------------------------------------------------*/\r
6662 \r
6663 /* Nonmodal error box */\r
6664 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6665                              WPARAM wParam, LPARAM lParam);\r
6666 \r
6667 VOID\r
6668 ErrorPopUp(char *title, char *content)\r
6669 {\r
6670   FARPROC lpProc;\r
6671   char *p, *q;\r
6672   BOOLEAN modal = hwndMain == NULL;\r
6673 \r
6674   p = content;\r
6675   q = errorMessage;\r
6676   while (*p) {\r
6677     if (*p == '\n') {\r
6678       if (modal) {\r
6679         *q++ = ' ';\r
6680         p++;\r
6681       } else {\r
6682         *q++ = '\r';\r
6683         *q++ = *p++;\r
6684       }\r
6685     } else {\r
6686       *q++ = *p++;\r
6687     }\r
6688   }\r
6689   *q = NULLCHAR;\r
6690   strncpy(errorTitle, title, sizeof(errorTitle));\r
6691   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6692   \r
6693   if (modal) {\r
6694     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6695   } else {\r
6696     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6697     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6698                  hwndMain, (DLGPROC)lpProc);\r
6699     FreeProcInstance(lpProc);\r
6700   }\r
6701 }\r
6702 \r
6703 VOID\r
6704 ErrorPopDown()\r
6705 {\r
6706   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6707   if (errorDialog == NULL) return;\r
6708   DestroyWindow(errorDialog);\r
6709   errorDialog = NULL;\r
6710   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6711 }\r
6712 \r
6713 LRESULT CALLBACK\r
6714 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6715 {\r
6716   HANDLE hwndText;\r
6717   RECT rChild;\r
6718 \r
6719   switch (message) {\r
6720   case WM_INITDIALOG:\r
6721     GetWindowRect(hDlg, &rChild);\r
6722 \r
6723     /*\r
6724     SetWindowPos(hDlg, NULL, rChild.left,\r
6725       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6726       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6727     */\r
6728 \r
6729     /* \r
6730         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6731         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6732         and it doesn't work when you resize the dialog.\r
6733         For now, just give it a default position.\r
6734     */\r
6735     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6736     Translate(hDlg, DLG_Error);\r
6737 \r
6738     errorDialog = hDlg;\r
6739     SetWindowText(hDlg, errorTitle);\r
6740     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6741     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6742     return FALSE;\r
6743 \r
6744   case WM_COMMAND:\r
6745     switch (LOWORD(wParam)) {\r
6746     case IDOK:\r
6747     case IDCANCEL:\r
6748       if (errorDialog == hDlg) errorDialog = NULL;\r
6749       DestroyWindow(hDlg);\r
6750       return TRUE;\r
6751 \r
6752     default:\r
6753       break;\r
6754     }\r
6755     break;\r
6756   }\r
6757   return FALSE;\r
6758 }\r
6759 \r
6760 #ifdef GOTHIC\r
6761 HWND gothicDialog = NULL;\r
6762 \r
6763 LRESULT CALLBACK\r
6764 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6765 {\r
6766   HANDLE hwndText;\r
6767   RECT rChild;\r
6768   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6769 \r
6770   switch (message) {\r
6771   case WM_INITDIALOG:\r
6772     GetWindowRect(hDlg, &rChild);\r
6773 \r
6774     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6775                                                              SWP_NOZORDER);\r
6776 \r
6777     /* \r
6778         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6779         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6780         and it doesn't work when you resize the dialog.\r
6781         For now, just give it a default position.\r
6782     */\r
6783     gothicDialog = hDlg;\r
6784     SetWindowText(hDlg, errorTitle);\r
6785     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6786     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6787     return FALSE;\r
6788 \r
6789   case WM_COMMAND:\r
6790     switch (LOWORD(wParam)) {\r
6791     case IDOK:\r
6792     case IDCANCEL:\r
6793       if (errorDialog == hDlg) errorDialog = NULL;\r
6794       DestroyWindow(hDlg);\r
6795       return TRUE;\r
6796 \r
6797     default:\r
6798       break;\r
6799     }\r
6800     break;\r
6801   }\r
6802   return FALSE;\r
6803 }\r
6804 \r
6805 VOID\r
6806 GothicPopUp(char *title, VariantClass variant)\r
6807 {\r
6808   FARPROC lpProc;\r
6809   static char *lastTitle;\r
6810 \r
6811   strncpy(errorTitle, title, sizeof(errorTitle));\r
6812   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6813 \r
6814   if(lastTitle != title && gothicDialog != NULL) {\r
6815     DestroyWindow(gothicDialog);\r
6816     gothicDialog = NULL;\r
6817   }\r
6818   if(variant != VariantNormal && gothicDialog == NULL) {\r
6819     title = lastTitle;\r
6820     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6821     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6822                  hwndMain, (DLGPROC)lpProc);\r
6823     FreeProcInstance(lpProc);\r
6824   }\r
6825 }\r
6826 #endif\r
6827 \r
6828 /*---------------------------------------------------------------------------*\\r
6829  *\r
6830  *  Ics Interaction console functions\r
6831  *\r
6832 \*---------------------------------------------------------------------------*/\r
6833 \r
6834 #define HISTORY_SIZE 64\r
6835 static char *history[HISTORY_SIZE];\r
6836 int histIn = 0, histP = 0;\r
6837 \r
6838 VOID\r
6839 SaveInHistory(char *cmd)\r
6840 {\r
6841   if (history[histIn] != NULL) {\r
6842     free(history[histIn]);\r
6843     history[histIn] = NULL;\r
6844   }\r
6845   if (*cmd == NULLCHAR) return;\r
6846   history[histIn] = StrSave(cmd);\r
6847   histIn = (histIn + 1) % HISTORY_SIZE;\r
6848   if (history[histIn] != NULL) {\r
6849     free(history[histIn]);\r
6850     history[histIn] = NULL;\r
6851   }\r
6852   histP = histIn;\r
6853 }\r
6854 \r
6855 char *\r
6856 PrevInHistory(char *cmd)\r
6857 {\r
6858   int newhp;\r
6859   if (histP == histIn) {\r
6860     if (history[histIn] != NULL) free(history[histIn]);\r
6861     history[histIn] = StrSave(cmd);\r
6862   }\r
6863   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6864   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6865   histP = newhp;\r
6866   return history[histP];\r
6867 }\r
6868 \r
6869 char *\r
6870 NextInHistory()\r
6871 {\r
6872   if (histP == histIn) return NULL;\r
6873   histP = (histP + 1) % HISTORY_SIZE;\r
6874   return history[histP];   \r
6875 }\r
6876 \r
6877 HMENU\r
6878 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6879 {\r
6880   HMENU hmenu, h;\r
6881   int i = 0;\r
6882   hmenu = LoadMenu(hInst, "TextMenu");\r
6883   h = GetSubMenu(hmenu, 0);\r
6884   while (e->item) {\r
6885     if (strcmp(e->item, "-") == 0) {\r
6886       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6887     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6888       int flags = MF_STRING, j = 0;\r
6889       if (e->item[0] == '|') {\r
6890         flags |= MF_MENUBARBREAK;\r
6891         j++;\r
6892       }\r
6893       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6894       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6895     }\r
6896     e++;\r
6897     i++;\r
6898   } \r
6899   return hmenu;\r
6900 }\r
6901 \r
6902 WNDPROC consoleTextWindowProc;\r
6903 \r
6904 void\r
6905 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6906 {\r
6907   char buf[MSG_SIZ], name[MSG_SIZ];\r
6908   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6909   CHARRANGE sel;\r
6910 \r
6911   if (!getname) {\r
6912     SetWindowText(hInput, command);\r
6913     if (immediate) {\r
6914       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6915     } else {\r
6916       sel.cpMin = 999999;\r
6917       sel.cpMax = 999999;\r
6918       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6919       SetFocus(hInput);\r
6920     }\r
6921     return;\r
6922   }    \r
6923   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6924   if (sel.cpMin == sel.cpMax) {\r
6925     /* Expand to surrounding word */\r
6926     TEXTRANGE tr;\r
6927     do {\r
6928       tr.chrg.cpMax = sel.cpMin;\r
6929       tr.chrg.cpMin = --sel.cpMin;\r
6930       if (sel.cpMin < 0) break;\r
6931       tr.lpstrText = name;\r
6932       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6933     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6934     sel.cpMin++;\r
6935 \r
6936     do {\r
6937       tr.chrg.cpMin = sel.cpMax;\r
6938       tr.chrg.cpMax = ++sel.cpMax;\r
6939       tr.lpstrText = name;\r
6940       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6941     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6942     sel.cpMax--;\r
6943 \r
6944     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6945       MessageBeep(MB_ICONEXCLAMATION);\r
6946       return;\r
6947     }\r
6948     tr.chrg = sel;\r
6949     tr.lpstrText = name;\r
6950     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6951   } else {\r
6952     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6953       MessageBeep(MB_ICONEXCLAMATION);\r
6954       return;\r
6955     }\r
6956     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6957   }\r
6958   if (immediate) {\r
6959     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6960     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6961     SetWindowText(hInput, buf);\r
6962     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6963   } else {\r
6964     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6965       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6966     SetWindowText(hInput, buf);\r
6967     sel.cpMin = 999999;\r
6968     sel.cpMax = 999999;\r
6969     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6970     SetFocus(hInput);\r
6971   }\r
6972 }\r
6973 \r
6974 LRESULT CALLBACK \r
6975 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6976 {\r
6977   HWND hInput;\r
6978   CHARRANGE sel;\r
6979 \r
6980   switch (message) {\r
6981   case WM_KEYDOWN:\r
6982     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6983     if(wParam=='R') return 0;\r
6984     switch (wParam) {\r
6985     case VK_PRIOR:\r
6986       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6987       return 0;\r
6988     case VK_NEXT:\r
6989       sel.cpMin = 999999;\r
6990       sel.cpMax = 999999;\r
6991       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6992       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6993       return 0;\r
6994     }\r
6995     break;\r
6996   case WM_CHAR:\r
6997    if(wParam != '\022') {\r
6998     if (wParam == '\t') {\r
6999       if (GetKeyState(VK_SHIFT) < 0) {\r
7000         /* shifted */\r
7001         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7002         if (buttonDesc[0].hwnd) {\r
7003           SetFocus(buttonDesc[0].hwnd);\r
7004         } else {\r
7005           SetFocus(hwndMain);\r
7006         }\r
7007       } else {\r
7008         /* unshifted */\r
7009         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7010       }\r
7011     } else {\r
7012       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7013       JAWS_DELETE( SetFocus(hInput); )\r
7014       SendMessage(hInput, message, wParam, lParam);\r
7015     }\r
7016     return 0;\r
7017    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7018    lParam = -1;\r
7019   case WM_RBUTTONDOWN:\r
7020     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7021       /* Move selection here if it was empty */\r
7022       POINT pt;\r
7023       pt.x = LOWORD(lParam);\r
7024       pt.y = HIWORD(lParam);\r
7025       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7026       if (sel.cpMin == sel.cpMax) {\r
7027         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7028         sel.cpMax = sel.cpMin;\r
7029         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7030       }\r
7031       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7032 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7033       POINT pt;\r
7034       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7035       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7036       if (sel.cpMin == sel.cpMax) {\r
7037         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7038         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7039       }\r
7040       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7041         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7042       }\r
7043       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7044       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7045       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7046       MenuPopup(hwnd, pt, hmenu, -1);\r
7047 }\r
7048     }\r
7049     return 0;\r
7050   case WM_RBUTTONUP:\r
7051     if (GetKeyState(VK_SHIFT) & ~1) {\r
7052       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7053         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7054     }\r
7055     return 0;\r
7056   case WM_PASTE:\r
7057     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7058     SetFocus(hInput);\r
7059     return SendMessage(hInput, message, wParam, lParam);\r
7060   case WM_MBUTTONDOWN:\r
7061     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7062   case WM_COMMAND:\r
7063     switch (LOWORD(wParam)) {\r
7064     case IDM_QuickPaste:\r
7065       {\r
7066         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7067         if (sel.cpMin == sel.cpMax) {\r
7068           MessageBeep(MB_ICONEXCLAMATION);\r
7069           return 0;\r
7070         }\r
7071         SendMessage(hwnd, WM_COPY, 0, 0);\r
7072         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7073         SendMessage(hInput, WM_PASTE, 0, 0);\r
7074         SetFocus(hInput);\r
7075         return 0;\r
7076       }\r
7077     case IDM_Cut:\r
7078       SendMessage(hwnd, WM_CUT, 0, 0);\r
7079       return 0;\r
7080     case IDM_Paste:\r
7081       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7082       return 0;\r
7083     case IDM_Copy:\r
7084       SendMessage(hwnd, WM_COPY, 0, 0);\r
7085       return 0;\r
7086     default:\r
7087       {\r
7088         int i = LOWORD(wParam) - IDM_CommandX;\r
7089         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7090             icsTextMenuEntry[i].command != NULL) {\r
7091           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7092                    icsTextMenuEntry[i].getname,\r
7093                    icsTextMenuEntry[i].immediate);\r
7094           return 0;\r
7095         }\r
7096       }\r
7097       break;\r
7098     }\r
7099     break;\r
7100   }\r
7101   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7102 }\r
7103 \r
7104 WNDPROC consoleInputWindowProc;\r
7105 \r
7106 LRESULT CALLBACK\r
7107 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7108 {\r
7109   char buf[MSG_SIZ];\r
7110   char *p;\r
7111   static BOOL sendNextChar = FALSE;\r
7112   static BOOL quoteNextChar = FALSE;\r
7113   InputSource *is = consoleInputSource;\r
7114   CHARFORMAT cf;\r
7115   CHARRANGE sel;\r
7116 \r
7117   switch (message) {\r
7118   case WM_CHAR:\r
7119     if (!appData.localLineEditing || sendNextChar) {\r
7120       is->buf[0] = (CHAR) wParam;\r
7121       is->count = 1;\r
7122       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7123       sendNextChar = FALSE;\r
7124       return 0;\r
7125     }\r
7126     if (quoteNextChar) {\r
7127       buf[0] = (char) wParam;\r
7128       buf[1] = NULLCHAR;\r
7129       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7130       quoteNextChar = FALSE;\r
7131       return 0;\r
7132     }\r
7133     switch (wParam) {\r
7134     case '\r':   /* Enter key */\r
7135       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7136       if (consoleEcho) SaveInHistory(is->buf);\r
7137       is->buf[is->count++] = '\n';\r
7138       is->buf[is->count] = NULLCHAR;\r
7139       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7140       if (consoleEcho) {\r
7141         ConsoleOutput(is->buf, is->count, TRUE);\r
7142       } else if (appData.localLineEditing) {\r
7143         ConsoleOutput("\n", 1, TRUE);\r
7144       }\r
7145       /* fall thru */\r
7146     case '\033': /* Escape key */\r
7147       SetWindowText(hwnd, "");\r
7148       cf.cbSize = sizeof(CHARFORMAT);\r
7149       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7150       if (consoleEcho) {\r
7151         cf.crTextColor = textAttribs[ColorNormal].color;\r
7152       } else {\r
7153         cf.crTextColor = COLOR_ECHOOFF;\r
7154       }\r
7155       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7156       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7157       return 0;\r
7158     case '\t':   /* Tab key */\r
7159       if (GetKeyState(VK_SHIFT) < 0) {\r
7160         /* shifted */\r
7161         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7162       } else {\r
7163         /* unshifted */\r
7164         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7165         if (buttonDesc[0].hwnd) {\r
7166           SetFocus(buttonDesc[0].hwnd);\r
7167         } else {\r
7168           SetFocus(hwndMain);\r
7169         }\r
7170       }\r
7171       return 0;\r
7172     case '\023': /* Ctrl+S */\r
7173       sendNextChar = TRUE;\r
7174       return 0;\r
7175     case '\021': /* Ctrl+Q */\r
7176       quoteNextChar = TRUE;\r
7177       return 0;\r
7178     JAWS_REPLAY\r
7179     default:\r
7180       break;\r
7181     }\r
7182     break;\r
7183   case WM_KEYDOWN:\r
7184     switch (wParam) {\r
7185     case VK_UP:\r
7186       GetWindowText(hwnd, buf, MSG_SIZ);\r
7187       p = PrevInHistory(buf);\r
7188       if (p != NULL) {\r
7189         SetWindowText(hwnd, p);\r
7190         sel.cpMin = 999999;\r
7191         sel.cpMax = 999999;\r
7192         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7193         return 0;\r
7194       }\r
7195       break;\r
7196     case VK_DOWN:\r
7197       p = NextInHistory();\r
7198       if (p != NULL) {\r
7199         SetWindowText(hwnd, p);\r
7200         sel.cpMin = 999999;\r
7201         sel.cpMax = 999999;\r
7202         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7203         return 0;\r
7204       }\r
7205       break;\r
7206     case VK_HOME:\r
7207     case VK_END:\r
7208       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7209       /* fall thru */\r
7210     case VK_PRIOR:\r
7211     case VK_NEXT:\r
7212       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7213       return 0;\r
7214     }\r
7215     break;\r
7216   case WM_MBUTTONDOWN:\r
7217     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7218       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7219     break;\r
7220   case WM_RBUTTONUP:\r
7221     if (GetKeyState(VK_SHIFT) & ~1) {\r
7222       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7223         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7224     } else {\r
7225       POINT pt;\r
7226       HMENU hmenu;\r
7227       hmenu = LoadMenu(hInst, "InputMenu");\r
7228       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7229       if (sel.cpMin == sel.cpMax) {\r
7230         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7231         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7232       }\r
7233       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7234         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7235       }\r
7236       pt.x = LOWORD(lParam);\r
7237       pt.y = HIWORD(lParam);\r
7238       MenuPopup(hwnd, pt, hmenu, -1);\r
7239     }\r
7240     return 0;\r
7241   case WM_COMMAND:\r
7242     switch (LOWORD(wParam)) { \r
7243     case IDM_Undo:\r
7244       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7245       return 0;\r
7246     case IDM_SelectAll:\r
7247       sel.cpMin = 0;\r
7248       sel.cpMax = -1; /*999999?*/\r
7249       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7250       return 0;\r
7251     case IDM_Cut:\r
7252       SendMessage(hwnd, WM_CUT, 0, 0);\r
7253       return 0;\r
7254     case IDM_Paste:\r
7255       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7256       return 0;\r
7257     case IDM_Copy:\r
7258       SendMessage(hwnd, WM_COPY, 0, 0);\r
7259       return 0;\r
7260     }\r
7261     break;\r
7262   }\r
7263   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7264 }\r
7265 \r
7266 #define CO_MAX  100000\r
7267 #define CO_TRIM   1000\r
7268 \r
7269 LRESULT CALLBACK\r
7270 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7271 {\r
7272   static SnapData sd;\r
7273   HWND hText, hInput;\r
7274   RECT rect;\r
7275   static int sizeX, sizeY;\r
7276   int newSizeX, newSizeY;\r
7277   MINMAXINFO *mmi;\r
7278   WORD wMask;\r
7279 \r
7280   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7281   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7282 \r
7283   switch (message) {\r
7284   case WM_NOTIFY:\r
7285     if (((NMHDR*)lParam)->code == EN_LINK)\r
7286     {\r
7287       ENLINK *pLink = (ENLINK*)lParam;\r
7288       if (pLink->msg == WM_LBUTTONUP)\r
7289       {\r
7290         TEXTRANGE tr;\r
7291 \r
7292         tr.chrg = pLink->chrg;\r
7293         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7294         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7295         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7296         free(tr.lpstrText);\r
7297       }\r
7298     }\r
7299     break;\r
7300   case WM_INITDIALOG: /* message: initialize dialog box */\r
7301     hwndConsole = hDlg;\r
7302     SetFocus(hInput);\r
7303     consoleTextWindowProc = (WNDPROC)\r
7304       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7305     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7306     consoleInputWindowProc = (WNDPROC)\r
7307       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7308     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7309     Colorize(ColorNormal, TRUE);\r
7310     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7311     ChangedConsoleFont();\r
7312     GetClientRect(hDlg, &rect);\r
7313     sizeX = rect.right;\r
7314     sizeY = rect.bottom;\r
7315     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7316         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7317       WINDOWPLACEMENT wp;\r
7318       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7319       wp.length = sizeof(WINDOWPLACEMENT);\r
7320       wp.flags = 0;\r
7321       wp.showCmd = SW_SHOW;\r
7322       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7323       wp.rcNormalPosition.left = wpConsole.x;\r
7324       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7325       wp.rcNormalPosition.top = wpConsole.y;\r
7326       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7327       SetWindowPlacement(hDlg, &wp);\r
7328     }\r
7329 \r
7330    // [HGM] Chessknight's change 2004-07-13\r
7331    else { /* Determine Defaults */\r
7332        WINDOWPLACEMENT wp;\r
7333        wpConsole.x = wpMain.width + 1;\r
7334        wpConsole.y = wpMain.y;\r
7335        wpConsole.width = screenWidth -  wpMain.width;\r
7336        wpConsole.height = wpMain.height;\r
7337        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7338        wp.length = sizeof(WINDOWPLACEMENT);\r
7339        wp.flags = 0;\r
7340        wp.showCmd = SW_SHOW;\r
7341        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7342        wp.rcNormalPosition.left = wpConsole.x;\r
7343        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7344        wp.rcNormalPosition.top = wpConsole.y;\r
7345        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7346        SetWindowPlacement(hDlg, &wp);\r
7347     }\r
7348 \r
7349    // Allow hText to highlight URLs and send notifications on them\r
7350    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7351    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7352    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7353    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7354 \r
7355     return FALSE;\r
7356 \r
7357   case WM_SETFOCUS:\r
7358     SetFocus(hInput);\r
7359     return 0;\r
7360 \r
7361   case WM_CLOSE:\r
7362     ExitEvent(0);\r
7363     /* not reached */\r
7364     break;\r
7365 \r
7366   case WM_SIZE:\r
7367     if (IsIconic(hDlg)) break;\r
7368     newSizeX = LOWORD(lParam);\r
7369     newSizeY = HIWORD(lParam);\r
7370     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7371       RECT rectText, rectInput;\r
7372       POINT pt;\r
7373       int newTextHeight, newTextWidth;\r
7374       GetWindowRect(hText, &rectText);\r
7375       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7376       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7377       if (newTextHeight < 0) {\r
7378         newSizeY += -newTextHeight;\r
7379         newTextHeight = 0;\r
7380       }\r
7381       SetWindowPos(hText, NULL, 0, 0,\r
7382         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7383       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7384       pt.x = rectInput.left;\r
7385       pt.y = rectInput.top + newSizeY - sizeY;\r
7386       ScreenToClient(hDlg, &pt);\r
7387       SetWindowPos(hInput, NULL, \r
7388         pt.x, pt.y, /* needs client coords */   \r
7389         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7390         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7391     }\r
7392     sizeX = newSizeX;\r
7393     sizeY = newSizeY;\r
7394     break;\r
7395 \r
7396   case WM_GETMINMAXINFO:\r
7397     /* Prevent resizing window too small */\r
7398     mmi = (MINMAXINFO *) lParam;\r
7399     mmi->ptMinTrackSize.x = 100;\r
7400     mmi->ptMinTrackSize.y = 100;\r
7401     break;\r
7402 \r
7403   /* [AS] Snapping */\r
7404   case WM_ENTERSIZEMOVE:\r
7405     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7406 \r
7407   case WM_SIZING:\r
7408     return OnSizing( &sd, hDlg, wParam, lParam );\r
7409 \r
7410   case WM_MOVING:\r
7411     return OnMoving( &sd, hDlg, wParam, lParam );\r
7412 \r
7413   case WM_EXITSIZEMOVE:\r
7414         UpdateICSWidth(hText);\r
7415     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7416   }\r
7417 \r
7418   return DefWindowProc(hDlg, message, wParam, lParam);\r
7419 }\r
7420 \r
7421 \r
7422 VOID\r
7423 ConsoleCreate()\r
7424 {\r
7425   HWND hCons;\r
7426   if (hwndConsole) return;\r
7427   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7428   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7429 }\r
7430 \r
7431 \r
7432 VOID\r
7433 ConsoleOutput(char* data, int length, int forceVisible)\r
7434 {\r
7435   HWND hText;\r
7436   int trim, exlen;\r
7437   char *p, *q;\r
7438   char buf[CO_MAX+1];\r
7439   POINT pEnd;\r
7440   RECT rect;\r
7441   static int delayLF = 0;\r
7442   CHARRANGE savesel, sel;\r
7443 \r
7444   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7445   p = data;\r
7446   q = buf;\r
7447   if (delayLF) {\r
7448     *q++ = '\r';\r
7449     *q++ = '\n';\r
7450     delayLF = 0;\r
7451   }\r
7452   while (length--) {\r
7453     if (*p == '\n') {\r
7454       if (*++p) {\r
7455         *q++ = '\r';\r
7456         *q++ = '\n';\r
7457       } else {\r
7458         delayLF = 1;\r
7459       }\r
7460     } else if (*p == '\007') {\r
7461        MyPlaySound(&sounds[(int)SoundBell]);\r
7462        p++;\r
7463     } else {\r
7464       *q++ = *p++;\r
7465     }\r
7466   }\r
7467   *q = NULLCHAR;\r
7468   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7469   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7470   /* Save current selection */\r
7471   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7472   exlen = GetWindowTextLength(hText);\r
7473   /* Find out whether current end of text is visible */\r
7474   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7475   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7476   /* Trim existing text if it's too long */\r
7477   if (exlen + (q - buf) > CO_MAX) {\r
7478     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7479     sel.cpMin = 0;\r
7480     sel.cpMax = trim;\r
7481     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7482     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7483     exlen -= trim;\r
7484     savesel.cpMin -= trim;\r
7485     savesel.cpMax -= trim;\r
7486     if (exlen < 0) exlen = 0;\r
7487     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7488     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7489   }\r
7490   /* Append the new text */\r
7491   sel.cpMin = exlen;\r
7492   sel.cpMax = exlen;\r
7493   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7494   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7495   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7496   if (forceVisible || exlen == 0 ||\r
7497       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7498        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7499     /* Scroll to make new end of text visible if old end of text\r
7500        was visible or new text is an echo of user typein */\r
7501     sel.cpMin = 9999999;\r
7502     sel.cpMax = 9999999;\r
7503     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7504     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7505     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7506     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7507   }\r
7508   if (savesel.cpMax == exlen || forceVisible) {\r
7509     /* Move insert point to new end of text if it was at the old\r
7510        end of text or if the new text is an echo of user typein */\r
7511     sel.cpMin = 9999999;\r
7512     sel.cpMax = 9999999;\r
7513     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7514   } else {\r
7515     /* Restore previous selection */\r
7516     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7517   }\r
7518   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7519 }\r
7520 \r
7521 /*---------*/\r
7522 \r
7523 \r
7524 void\r
7525 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7526 {\r
7527   char buf[100];\r
7528   char *str;\r
7529   COLORREF oldFg, oldBg;\r
7530   HFONT oldFont;\r
7531   RECT rect;\r
7532 \r
7533   if(copyNumber > 1)\r
7534     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7535 \r
7536   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7537   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7538   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7539 \r
7540   rect.left = x;\r
7541   rect.right = x + squareSize;\r
7542   rect.top  = y;\r
7543   rect.bottom = y + squareSize;\r
7544   str = buf;\r
7545 \r
7546   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7547                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7548              y, ETO_CLIPPED|ETO_OPAQUE,\r
7549              &rect, str, strlen(str), NULL);\r
7550 \r
7551   (void) SetTextColor(hdc, oldFg);\r
7552   (void) SetBkColor(hdc, oldBg);\r
7553   (void) SelectObject(hdc, oldFont);\r
7554 }\r
7555 \r
7556 void\r
7557 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7558               RECT *rect, char *color, char *flagFell)\r
7559 {\r
7560   char buf[100];\r
7561   char *str;\r
7562   COLORREF oldFg, oldBg;\r
7563   HFONT oldFont;\r
7564 \r
7565   if (twoBoards && partnerUp) return;\r
7566   if (appData.clockMode) {\r
7567     if (tinyLayout)\r
7568       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7569     else\r
7570       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7571     str = buf;\r
7572   } else {\r
7573     str = color;\r
7574   }\r
7575 \r
7576   if (highlight) {\r
7577     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7578     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7579   } else {\r
7580     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7581     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7582   }\r
7583   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7584 \r
7585   JAWS_SILENCE\r
7586 \r
7587   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7588              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7589              rect, str, strlen(str), NULL);\r
7590   if(logoHeight > 0 && appData.clockMode) {\r
7591       RECT r;\r
7592       str += strlen(color)+2;\r
7593       r.top = rect->top + logoHeight/2;\r
7594       r.left = rect->left;\r
7595       r.right = rect->right;\r
7596       r.bottom = rect->bottom;\r
7597       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7598                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7599                  &r, str, strlen(str), NULL);\r
7600   }\r
7601   (void) SetTextColor(hdc, oldFg);\r
7602   (void) SetBkColor(hdc, oldBg);\r
7603   (void) SelectObject(hdc, oldFont);\r
7604 }\r
7605 \r
7606 \r
7607 int\r
7608 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7609            OVERLAPPED *ovl)\r
7610 {\r
7611   int ok, err;\r
7612 \r
7613   /* [AS]  */\r
7614   if( count <= 0 ) {\r
7615     if (appData.debugMode) {\r
7616       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7617     }\r
7618 \r
7619     return ERROR_INVALID_USER_BUFFER;\r
7620   }\r
7621 \r
7622   ResetEvent(ovl->hEvent);\r
7623   ovl->Offset = ovl->OffsetHigh = 0;\r
7624   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7625   if (ok) {\r
7626     err = NO_ERROR;\r
7627   } else {\r
7628     err = GetLastError();\r
7629     if (err == ERROR_IO_PENDING) {\r
7630       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7631       if (ok)\r
7632         err = NO_ERROR;\r
7633       else\r
7634         err = GetLastError();\r
7635     }\r
7636   }\r
7637   return err;\r
7638 }\r
7639 \r
7640 int\r
7641 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7642             OVERLAPPED *ovl)\r
7643 {\r
7644   int ok, err;\r
7645 \r
7646   ResetEvent(ovl->hEvent);\r
7647   ovl->Offset = ovl->OffsetHigh = 0;\r
7648   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7649   if (ok) {\r
7650     err = NO_ERROR;\r
7651   } else {\r
7652     err = GetLastError();\r
7653     if (err == ERROR_IO_PENDING) {\r
7654       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7655       if (ok)\r
7656         err = NO_ERROR;\r
7657       else\r
7658         err = GetLastError();\r
7659     }\r
7660   }\r
7661   return err;\r
7662 }\r
7663 \r
7664 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7665 void CheckForInputBufferFull( InputSource * is )\r
7666 {\r
7667     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7668         /* Look for end of line */\r
7669         char * p = is->buf;\r
7670         \r
7671         while( p < is->next && *p != '\n' ) {\r
7672             p++;\r
7673         }\r
7674 \r
7675         if( p >= is->next ) {\r
7676             if (appData.debugMode) {\r
7677                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7678             }\r
7679 \r
7680             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7681             is->count = (DWORD) -1;\r
7682             is->next = is->buf;\r
7683         }\r
7684     }\r
7685 }\r
7686 \r
7687 DWORD\r
7688 InputThread(LPVOID arg)\r
7689 {\r
7690   InputSource *is;\r
7691   OVERLAPPED ovl;\r
7692 \r
7693   is = (InputSource *) arg;\r
7694   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7695   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7696   while (is->hThread != NULL) {\r
7697     is->error = DoReadFile(is->hFile, is->next,\r
7698                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7699                            &is->count, &ovl);\r
7700     if (is->error == NO_ERROR) {\r
7701       is->next += is->count;\r
7702     } else {\r
7703       if (is->error == ERROR_BROKEN_PIPE) {\r
7704         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7705         is->count = 0;\r
7706       } else {\r
7707         is->count = (DWORD) -1;\r
7708         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7709         break; \r
7710       }\r
7711     }\r
7712 \r
7713     CheckForInputBufferFull( is );\r
7714 \r
7715     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7716 \r
7717     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7718 \r
7719     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7720   }\r
7721 \r
7722   CloseHandle(ovl.hEvent);\r
7723   CloseHandle(is->hFile);\r
7724 \r
7725   if (appData.debugMode) {\r
7726     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7727   }\r
7728 \r
7729   return 0;\r
7730 }\r
7731 \r
7732 \r
7733 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7734 DWORD\r
7735 NonOvlInputThread(LPVOID arg)\r
7736 {\r
7737   InputSource *is;\r
7738   char *p, *q;\r
7739   int i;\r
7740   char prev;\r
7741 \r
7742   is = (InputSource *) arg;\r
7743   while (is->hThread != NULL) {\r
7744     is->error = ReadFile(is->hFile, is->next,\r
7745                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7746                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7747     if (is->error == NO_ERROR) {\r
7748       /* Change CRLF to LF */\r
7749       if (is->next > is->buf) {\r
7750         p = is->next - 1;\r
7751         i = is->count + 1;\r
7752       } else {\r
7753         p = is->next;\r
7754         i = is->count;\r
7755       }\r
7756       q = p;\r
7757       prev = NULLCHAR;\r
7758       while (i > 0) {\r
7759         if (prev == '\r' && *p == '\n') {\r
7760           *(q-1) = '\n';\r
7761           is->count--;\r
7762         } else { \r
7763           *q++ = *p;\r
7764         }\r
7765         prev = *p++;\r
7766         i--;\r
7767       }\r
7768       *q = NULLCHAR;\r
7769       is->next = q;\r
7770     } else {\r
7771       if (is->error == ERROR_BROKEN_PIPE) {\r
7772         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7773         is->count = 0; \r
7774       } else {\r
7775         is->count = (DWORD) -1;\r
7776       }\r
7777     }\r
7778 \r
7779     CheckForInputBufferFull( is );\r
7780 \r
7781     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7782 \r
7783     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7784 \r
7785     if (is->count < 0) break;  /* Quit on error */\r
7786   }\r
7787   CloseHandle(is->hFile);\r
7788   return 0;\r
7789 }\r
7790 \r
7791 DWORD\r
7792 SocketInputThread(LPVOID arg)\r
7793 {\r
7794   InputSource *is;\r
7795 \r
7796   is = (InputSource *) arg;\r
7797   while (is->hThread != NULL) {\r
7798     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7799     if ((int)is->count == SOCKET_ERROR) {\r
7800       is->count = (DWORD) -1;\r
7801       is->error = WSAGetLastError();\r
7802     } else {\r
7803       is->error = NO_ERROR;\r
7804       is->next += is->count;\r
7805       if (is->count == 0 && is->second == is) {\r
7806         /* End of file on stderr; quit with no message */\r
7807         break;\r
7808       }\r
7809     }\r
7810     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7811 \r
7812     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7813 \r
7814     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7815   }\r
7816   return 0;\r
7817 }\r
7818 \r
7819 VOID\r
7820 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7821 {\r
7822   InputSource *is;\r
7823 \r
7824   is = (InputSource *) lParam;\r
7825   if (is->lineByLine) {\r
7826     /* Feed in lines one by one */\r
7827     char *p = is->buf;\r
7828     char *q = p;\r
7829     while (q < is->next) {\r
7830       if (*q++ == '\n') {\r
7831         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7832         p = q;\r
7833       }\r
7834     }\r
7835     \r
7836     /* Move any partial line to the start of the buffer */\r
7837     q = is->buf;\r
7838     while (p < is->next) {\r
7839       *q++ = *p++;\r
7840     }\r
7841     is->next = q;\r
7842 \r
7843     if (is->error != NO_ERROR || is->count == 0) {\r
7844       /* Notify backend of the error.  Note: If there was a partial\r
7845          line at the end, it is not flushed through. */\r
7846       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7847     }\r
7848   } else {\r
7849     /* Feed in the whole chunk of input at once */\r
7850     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7851     is->next = is->buf;\r
7852   }\r
7853 }\r
7854 \r
7855 /*---------------------------------------------------------------------------*\\r
7856  *\r
7857  *  Menu enables. Used when setting various modes.\r
7858  *\r
7859 \*---------------------------------------------------------------------------*/\r
7860 \r
7861 typedef struct {\r
7862   int item;\r
7863   int flags;\r
7864 } Enables;\r
7865 \r
7866 VOID\r
7867 GreyRevert(Boolean grey)\r
7868 { // [HGM] vari: for retracting variations in local mode\r
7869   HMENU hmenu = GetMenu(hwndMain);\r
7870   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7871   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7872 }\r
7873 \r
7874 VOID\r
7875 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7876 {\r
7877   while (enab->item > 0) {\r
7878     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7879     enab++;\r
7880   }\r
7881 }\r
7882 \r
7883 Enables gnuEnables[] = {\r
7884   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7897 \r
7898   // Needed to switch from ncp to GNU mode on Engine Load\r
7899   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7900   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7901   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7913   { -1, -1 }\r
7914 };\r
7915 \r
7916 Enables icsEnables[] = {\r
7917   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7927   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7929   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7937   { -1, -1 }\r
7938 };\r
7939 \r
7940 #if ZIPPY\r
7941 Enables zippyEnables[] = {\r
7942   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7943   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7944   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7945   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7946   { -1, -1 }\r
7947 };\r
7948 #endif\r
7949 \r
7950 Enables ncpEnables[] = {\r
7951   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7960   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7961   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7973   { -1, -1 }\r
7974 };\r
7975 \r
7976 Enables trainingOnEnables[] = {\r
7977   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7981   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7986   { -1, -1 }\r
7987 };\r
7988 \r
7989 Enables trainingOffEnables[] = {\r
7990   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7991   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7992   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7993   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7994   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7995   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7996   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7997   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7998   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7999   { -1, -1 }\r
8000 };\r
8001 \r
8002 /* These modify either ncpEnables or gnuEnables */\r
8003 Enables cmailEnables[] = {\r
8004   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8005   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8006   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8007   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8009   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8011   { -1, -1 }\r
8012 };\r
8013 \r
8014 Enables machineThinkingEnables[] = {\r
8015   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8016   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8017   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8018   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8019   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8020   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8027   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8028 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8031   { -1, -1 }\r
8032 };\r
8033 \r
8034 Enables userThinkingEnables[] = {\r
8035   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8042   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8043   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8044   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8045   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8046   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8047   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8048 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8049   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8051   { -1, -1 }\r
8052 };\r
8053 \r
8054 /*---------------------------------------------------------------------------*\\r
8055  *\r
8056  *  Front-end interface functions exported by XBoard.\r
8057  *  Functions appear in same order as prototypes in frontend.h.\r
8058  * \r
8059 \*---------------------------------------------------------------------------*/\r
8060 VOID\r
8061 CheckMark(UINT item, int state)\r
8062 {\r
8063     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8064 }\r
8065 \r
8066 VOID\r
8067 ModeHighlight()\r
8068 {\r
8069   static UINT prevChecked = 0;\r
8070   static int prevPausing = 0;\r
8071   UINT nowChecked;\r
8072 \r
8073   if (pausing != prevPausing) {\r
8074     prevPausing = pausing;\r
8075     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8076                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8077     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8078   }\r
8079 \r
8080   switch (gameMode) {\r
8081   case BeginningOfGame:\r
8082     if (appData.icsActive)\r
8083       nowChecked = IDM_IcsClient;\r
8084     else if (appData.noChessProgram)\r
8085       nowChecked = IDM_EditGame;\r
8086     else\r
8087       nowChecked = IDM_MachineBlack;\r
8088     break;\r
8089   case MachinePlaysBlack:\r
8090     nowChecked = IDM_MachineBlack;\r
8091     break;\r
8092   case MachinePlaysWhite:\r
8093     nowChecked = IDM_MachineWhite;\r
8094     break;\r
8095   case TwoMachinesPlay:\r
8096     nowChecked = IDM_TwoMachines;\r
8097     break;\r
8098   case AnalyzeMode:\r
8099     nowChecked = IDM_AnalysisMode;\r
8100     break;\r
8101   case AnalyzeFile:\r
8102     nowChecked = IDM_AnalyzeFile;\r
8103     break;\r
8104   case EditGame:\r
8105     nowChecked = IDM_EditGame;\r
8106     break;\r
8107   case PlayFromGameFile:\r
8108     nowChecked = IDM_LoadGame;\r
8109     break;\r
8110   case EditPosition:\r
8111     nowChecked = IDM_EditPosition;\r
8112     break;\r
8113   case Training:\r
8114     nowChecked = IDM_Training;\r
8115     break;\r
8116   case IcsPlayingWhite:\r
8117   case IcsPlayingBlack:\r
8118   case IcsObserving:\r
8119   case IcsIdle:\r
8120     nowChecked = IDM_IcsClient;\r
8121     break;\r
8122   default:\r
8123   case EndOfGame:\r
8124     nowChecked = 0;\r
8125     break;\r
8126   }\r
8127   CheckMark(prevChecked, MF_UNCHECKED);\r
8128   CheckMark(nowChecked, MF_CHECKED);\r
8129   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8130 \r
8131   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8132     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8133                           MF_BYCOMMAND|MF_ENABLED);\r
8134   } else {\r
8135     (void) EnableMenuItem(GetMenu(hwndMain), \r
8136                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8137   }\r
8138 \r
8139   prevChecked = nowChecked;\r
8140 \r
8141   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8142   if (appData.icsActive) {\r
8143        if (appData.icsEngineAnalyze) {\r
8144                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8145        } else {\r
8146                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8147        }\r
8148   }\r
8149   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8150 }\r
8151 \r
8152 VOID\r
8153 SetICSMode()\r
8154 {\r
8155   HMENU hmenu = GetMenu(hwndMain);\r
8156   SetMenuEnables(hmenu, icsEnables);\r
8157   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8158     MF_BYCOMMAND|MF_ENABLED);\r
8159 #if ZIPPY\r
8160   if (appData.zippyPlay) {\r
8161     SetMenuEnables(hmenu, zippyEnables);\r
8162     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8163          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8164           MF_BYCOMMAND|MF_ENABLED);\r
8165   }\r
8166 #endif\r
8167 }\r
8168 \r
8169 VOID\r
8170 SetGNUMode()\r
8171 {\r
8172   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8173 }\r
8174 \r
8175 VOID\r
8176 SetNCPMode()\r
8177 {\r
8178   HMENU hmenu = GetMenu(hwndMain);\r
8179   SetMenuEnables(hmenu, ncpEnables);\r
8180     DrawMenuBar(hwndMain);\r
8181 }\r
8182 \r
8183 VOID\r
8184 SetCmailMode()\r
8185 {\r
8186   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8187 }\r
8188 \r
8189 VOID \r
8190 SetTrainingModeOn()\r
8191 {\r
8192   int i;\r
8193   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8194   for (i = 0; i < N_BUTTONS; i++) {\r
8195     if (buttonDesc[i].hwnd != NULL)\r
8196       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8197   }\r
8198   CommentPopDown();\r
8199 }\r
8200 \r
8201 VOID SetTrainingModeOff()\r
8202 {\r
8203   int i;\r
8204   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8205   for (i = 0; i < N_BUTTONS; i++) {\r
8206     if (buttonDesc[i].hwnd != NULL)\r
8207       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8208   }\r
8209 }\r
8210 \r
8211 \r
8212 VOID\r
8213 SetUserThinkingEnables()\r
8214 {\r
8215   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8216 }\r
8217 \r
8218 VOID\r
8219 SetMachineThinkingEnables()\r
8220 {\r
8221   HMENU hMenu = GetMenu(hwndMain);\r
8222   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8223 \r
8224   SetMenuEnables(hMenu, machineThinkingEnables);\r
8225 \r
8226   if (gameMode == MachinePlaysBlack) {\r
8227     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8228   } else if (gameMode == MachinePlaysWhite) {\r
8229     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8230   } else if (gameMode == TwoMachinesPlay) {\r
8231     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8232   }\r
8233 }\r
8234 \r
8235 \r
8236 VOID\r
8237 DisplayTitle(char *str)\r
8238 {\r
8239   char title[MSG_SIZ], *host;\r
8240   if (str[0] != NULLCHAR) {\r
8241     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8242   } else if (appData.icsActive) {\r
8243     if (appData.icsCommPort[0] != NULLCHAR)\r
8244       host = "ICS";\r
8245     else \r
8246       host = appData.icsHost;\r
8247       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8248   } else if (appData.noChessProgram) {\r
8249     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8250   } else {\r
8251     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8252     strcat(title, ": ");\r
8253     strcat(title, first.tidy);\r
8254   }\r
8255   SetWindowText(hwndMain, title);\r
8256 }\r
8257 \r
8258 \r
8259 VOID\r
8260 DisplayMessage(char *str1, char *str2)\r
8261 {\r
8262   HDC hdc;\r
8263   HFONT oldFont;\r
8264   int remain = MESSAGE_TEXT_MAX - 1;\r
8265   int len;\r
8266 \r
8267   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8268   messageText[0] = NULLCHAR;\r
8269   if (*str1) {\r
8270     len = strlen(str1);\r
8271     if (len > remain) len = remain;\r
8272     strncpy(messageText, str1, len);\r
8273     messageText[len] = NULLCHAR;\r
8274     remain -= len;\r
8275   }\r
8276   if (*str2 && remain >= 2) {\r
8277     if (*str1) {\r
8278       strcat(messageText, "  ");\r
8279       remain -= 2;\r
8280     }\r
8281     len = strlen(str2);\r
8282     if (len > remain) len = remain;\r
8283     strncat(messageText, str2, len);\r
8284   }\r
8285   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8286   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8287 \r
8288   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8289 \r
8290   SAYMACHINEMOVE();\r
8291 \r
8292   hdc = GetDC(hwndMain);\r
8293   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8294   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8295              &messageRect, messageText, strlen(messageText), NULL);\r
8296   (void) SelectObject(hdc, oldFont);\r
8297   (void) ReleaseDC(hwndMain, hdc);\r
8298 }\r
8299 \r
8300 VOID\r
8301 DisplayError(char *str, int error)\r
8302 {\r
8303   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8304   int len;\r
8305 \r
8306   if (error == 0) {\r
8307     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8308   } else {\r
8309     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8310                         NULL, error, LANG_NEUTRAL,\r
8311                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8312     if (len > 0) {\r
8313       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8314     } else {\r
8315       ErrorMap *em = errmap;\r
8316       while (em->err != 0 && em->err != error) em++;\r
8317       if (em->err != 0) {\r
8318         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8319       } else {\r
8320         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8321       }\r
8322     }\r
8323   }\r
8324   \r
8325   ErrorPopUp(_("Error"), buf);\r
8326 }\r
8327 \r
8328 \r
8329 VOID\r
8330 DisplayMoveError(char *str)\r
8331 {\r
8332   fromX = fromY = -1;\r
8333   ClearHighlights();\r
8334   DrawPosition(FALSE, NULL);\r
8335   if (appData.popupMoveErrors) {\r
8336     ErrorPopUp(_("Error"), str);\r
8337   } else {\r
8338     DisplayMessage(str, "");\r
8339     moveErrorMessageUp = TRUE;\r
8340   }\r
8341 }\r
8342 \r
8343 VOID\r
8344 DisplayFatalError(char *str, int error, int exitStatus)\r
8345 {\r
8346   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8347   int len;\r
8348   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8349 \r
8350   if (error != 0) {\r
8351     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8352                         NULL, error, LANG_NEUTRAL,\r
8353                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8354     if (len > 0) {\r
8355       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8356     } else {\r
8357       ErrorMap *em = errmap;\r
8358       while (em->err != 0 && em->err != error) em++;\r
8359       if (em->err != 0) {\r
8360         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8361       } else {\r
8362         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8363       }\r
8364     }\r
8365     str = buf;\r
8366   }\r
8367   if (appData.debugMode) {\r
8368     fprintf(debugFP, "%s: %s\n", label, str);\r
8369   }\r
8370   if (appData.popupExitMessage) {\r
8371     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8372                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8373   }\r
8374   ExitEvent(exitStatus);\r
8375 }\r
8376 \r
8377 \r
8378 VOID\r
8379 DisplayInformation(char *str)\r
8380 {\r
8381   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8382 }\r
8383 \r
8384 \r
8385 VOID\r
8386 DisplayNote(char *str)\r
8387 {\r
8388   ErrorPopUp(_("Note"), str);\r
8389 }\r
8390 \r
8391 \r
8392 typedef struct {\r
8393   char *title, *question, *replyPrefix;\r
8394   ProcRef pr;\r
8395 } QuestionParams;\r
8396 \r
8397 LRESULT CALLBACK\r
8398 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8399 {\r
8400   static QuestionParams *qp;\r
8401   char reply[MSG_SIZ];\r
8402   int len, err;\r
8403 \r
8404   switch (message) {\r
8405   case WM_INITDIALOG:\r
8406     qp = (QuestionParams *) lParam;\r
8407     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8408     Translate(hDlg, DLG_Question);\r
8409     SetWindowText(hDlg, qp->title);\r
8410     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8411     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8412     return FALSE;\r
8413 \r
8414   case WM_COMMAND:\r
8415     switch (LOWORD(wParam)) {\r
8416     case IDOK:\r
8417       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8418       if (*reply) strcat(reply, " ");\r
8419       len = strlen(reply);\r
8420       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8421       strcat(reply, "\n");\r
8422       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8423       EndDialog(hDlg, TRUE);\r
8424       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8425       return TRUE;\r
8426     case IDCANCEL:\r
8427       EndDialog(hDlg, FALSE);\r
8428       return TRUE;\r
8429     default:\r
8430       break;\r
8431     }\r
8432     break;\r
8433   }\r
8434   return FALSE;\r
8435 }\r
8436 \r
8437 VOID\r
8438 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8439 {\r
8440     QuestionParams qp;\r
8441     FARPROC lpProc;\r
8442     \r
8443     qp.title = title;\r
8444     qp.question = question;\r
8445     qp.replyPrefix = replyPrefix;\r
8446     qp.pr = pr;\r
8447     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8448     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8449       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8450     FreeProcInstance(lpProc);\r
8451 }\r
8452 \r
8453 /* [AS] Pick FRC position */\r
8454 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8455 {\r
8456     static int * lpIndexFRC;\r
8457     BOOL index_is_ok;\r
8458     char buf[16];\r
8459 \r
8460     switch( message )\r
8461     {\r
8462     case WM_INITDIALOG:\r
8463         lpIndexFRC = (int *) lParam;\r
8464 \r
8465         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8466         Translate(hDlg, DLG_NewGameFRC);\r
8467 \r
8468         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8469         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8470         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8471         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8472 \r
8473         break;\r
8474 \r
8475     case WM_COMMAND:\r
8476         switch( LOWORD(wParam) ) {\r
8477         case IDOK:\r
8478             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8479             EndDialog( hDlg, 0 );\r
8480             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8481             return TRUE;\r
8482         case IDCANCEL:\r
8483             EndDialog( hDlg, 1 );   \r
8484             return TRUE;\r
8485         case IDC_NFG_Edit:\r
8486             if( HIWORD(wParam) == EN_CHANGE ) {\r
8487                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8488 \r
8489                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8490             }\r
8491             return TRUE;\r
8492         case IDC_NFG_Random:\r
8493           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8494             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8495             return TRUE;\r
8496         }\r
8497 \r
8498         break;\r
8499     }\r
8500 \r
8501     return FALSE;\r
8502 }\r
8503 \r
8504 int NewGameFRC()\r
8505 {\r
8506     int result;\r
8507     int index = appData.defaultFrcPosition;\r
8508     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8509 \r
8510     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8511 \r
8512     if( result == 0 ) {\r
8513         appData.defaultFrcPosition = index;\r
8514     }\r
8515 \r
8516     return result;\r
8517 }\r
8518 \r
8519 /* [AS] Game list options. Refactored by HGM */\r
8520 \r
8521 HWND gameListOptionsDialog;\r
8522 \r
8523 // low-level front-end: clear text edit / list widget\r
8524 void\r
8525 GLT_ClearList()\r
8526 {\r
8527     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8528 }\r
8529 \r
8530 // low-level front-end: clear text edit / list widget\r
8531 void\r
8532 GLT_DeSelectList()\r
8533 {\r
8534     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8535 }\r
8536 \r
8537 // low-level front-end: append line to text edit / list widget\r
8538 void\r
8539 GLT_AddToList( char *name )\r
8540 {\r
8541     if( name != 0 ) {\r
8542             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8543     }\r
8544 }\r
8545 \r
8546 // low-level front-end: get line from text edit / list widget\r
8547 Boolean\r
8548 GLT_GetFromList( int index, char *name )\r
8549 {\r
8550     if( name != 0 ) {\r
8551             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8552                 return TRUE;\r
8553     }\r
8554     return FALSE;\r
8555 }\r
8556 \r
8557 void GLT_MoveSelection( HWND hDlg, int delta )\r
8558 {\r
8559     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8560     int idx2 = idx1 + delta;\r
8561     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8562 \r
8563     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8564         char buf[128];\r
8565 \r
8566         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8567         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8568         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8569         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8570     }\r
8571 }\r
8572 \r
8573 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8574 {\r
8575     switch( message )\r
8576     {\r
8577     case WM_INITDIALOG:\r
8578         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8579         \r
8580         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8581         Translate(hDlg, DLG_GameListOptions);\r
8582 \r
8583         /* Initialize list */\r
8584         GLT_TagsToList( lpUserGLT );\r
8585 \r
8586         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8587 \r
8588         break;\r
8589 \r
8590     case WM_COMMAND:\r
8591         switch( LOWORD(wParam) ) {\r
8592         case IDOK:\r
8593             GLT_ParseList();\r
8594             EndDialog( hDlg, 0 );\r
8595             return TRUE;\r
8596         case IDCANCEL:\r
8597             EndDialog( hDlg, 1 );\r
8598             return TRUE;\r
8599 \r
8600         case IDC_GLT_Default:\r
8601             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8602             return TRUE;\r
8603 \r
8604         case IDC_GLT_Restore:\r
8605             GLT_TagsToList( appData.gameListTags );\r
8606             return TRUE;\r
8607 \r
8608         case IDC_GLT_Up:\r
8609             GLT_MoveSelection( hDlg, -1 );\r
8610             return TRUE;\r
8611 \r
8612         case IDC_GLT_Down:\r
8613             GLT_MoveSelection( hDlg, +1 );\r
8614             return TRUE;\r
8615         }\r
8616 \r
8617         break;\r
8618     }\r
8619 \r
8620     return FALSE;\r
8621 }\r
8622 \r
8623 int GameListOptions()\r
8624 {\r
8625     int result;\r
8626     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8627 \r
8628       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8629 \r
8630     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8631 \r
8632     if( result == 0 ) {\r
8633         /* [AS] Memory leak here! */\r
8634         appData.gameListTags = strdup( lpUserGLT ); \r
8635     }\r
8636 \r
8637     return result;\r
8638 }\r
8639 \r
8640 VOID\r
8641 DisplayIcsInteractionTitle(char *str)\r
8642 {\r
8643   char consoleTitle[MSG_SIZ];\r
8644 \r
8645     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8646     SetWindowText(hwndConsole, consoleTitle);\r
8647 \r
8648     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8649       char buf[MSG_SIZ], *p = buf, *q;\r
8650         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8651       do {\r
8652         q = strchr(p, ';');\r
8653         if(q) *q++ = 0;\r
8654         if(*p) ChatPopUp(p);\r
8655       } while(p=q);\r
8656     }\r
8657 \r
8658     SetActiveWindow(hwndMain);\r
8659 }\r
8660 \r
8661 void\r
8662 DrawPosition(int fullRedraw, Board board)\r
8663 {\r
8664   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8665 }\r
8666 \r
8667 void NotifyFrontendLogin()\r
8668 {\r
8669         if (hwndConsole)\r
8670                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8671 }\r
8672 \r
8673 VOID\r
8674 ResetFrontEnd()\r
8675 {\r
8676   fromX = fromY = -1;\r
8677   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8678     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8679     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8680     dragInfo.lastpos = dragInfo.pos;\r
8681     dragInfo.start.x = dragInfo.start.y = -1;\r
8682     dragInfo.from = dragInfo.start;\r
8683     ReleaseCapture();\r
8684     DrawPosition(TRUE, NULL);\r
8685   }\r
8686   TagsPopDown();\r
8687 }\r
8688 \r
8689 \r
8690 VOID\r
8691 CommentPopUp(char *title, char *str)\r
8692 {\r
8693   HWND hwnd = GetActiveWindow();\r
8694   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8695   SAY(str);\r
8696   SetActiveWindow(hwnd);\r
8697 }\r
8698 \r
8699 VOID\r
8700 CommentPopDown(void)\r
8701 {\r
8702   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8703   if (commentDialog) {\r
8704     ShowWindow(commentDialog, SW_HIDE);\r
8705   }\r
8706   commentUp = FALSE;\r
8707 }\r
8708 \r
8709 VOID\r
8710 EditCommentPopUp(int index, char *title, char *str)\r
8711 {\r
8712   EitherCommentPopUp(index, title, str, TRUE);\r
8713 }\r
8714 \r
8715 \r
8716 VOID\r
8717 RingBell()\r
8718 {\r
8719   MyPlaySound(&sounds[(int)SoundMove]);\r
8720 }\r
8721 \r
8722 VOID PlayIcsWinSound()\r
8723 {\r
8724   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8725 }\r
8726 \r
8727 VOID PlayIcsLossSound()\r
8728 {\r
8729   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8730 }\r
8731 \r
8732 VOID PlayIcsDrawSound()\r
8733 {\r
8734   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8735 }\r
8736 \r
8737 VOID PlayIcsUnfinishedSound()\r
8738 {\r
8739   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8740 }\r
8741 \r
8742 VOID\r
8743 PlayAlarmSound()\r
8744 {\r
8745   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8746 }\r
8747 \r
8748 VOID\r
8749 PlayTellSound()\r
8750 {\r
8751   MyPlaySound(&textAttribs[ColorTell].sound);\r
8752 }\r
8753 \r
8754 \r
8755 VOID\r
8756 EchoOn()\r
8757 {\r
8758   HWND hInput;\r
8759   consoleEcho = TRUE;\r
8760   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8761   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8762   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8763 }\r
8764 \r
8765 \r
8766 VOID\r
8767 EchoOff()\r
8768 {\r
8769   CHARFORMAT cf;\r
8770   HWND hInput;\r
8771   consoleEcho = FALSE;\r
8772   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8773   /* This works OK: set text and background both to the same color */\r
8774   cf = consoleCF;\r
8775   cf.crTextColor = COLOR_ECHOOFF;\r
8776   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8777   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8778 }\r
8779 \r
8780 /* No Raw()...? */\r
8781 \r
8782 void Colorize(ColorClass cc, int continuation)\r
8783 {\r
8784   currentColorClass = cc;\r
8785   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8786   consoleCF.crTextColor = textAttribs[cc].color;\r
8787   consoleCF.dwEffects = textAttribs[cc].effects;\r
8788   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8789 }\r
8790 \r
8791 char *\r
8792 UserName()\r
8793 {\r
8794   static char buf[MSG_SIZ];\r
8795   DWORD bufsiz = MSG_SIZ;\r
8796 \r
8797   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8798         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8799   }\r
8800   if (!GetUserName(buf, &bufsiz)) {\r
8801     /*DisplayError("Error getting user name", GetLastError());*/\r
8802     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8803   }\r
8804   return buf;\r
8805 }\r
8806 \r
8807 char *\r
8808 HostName()\r
8809 {\r
8810   static char buf[MSG_SIZ];\r
8811   DWORD bufsiz = MSG_SIZ;\r
8812 \r
8813   if (!GetComputerName(buf, &bufsiz)) {\r
8814     /*DisplayError("Error getting host name", GetLastError());*/\r
8815     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8816   }\r
8817   return buf;\r
8818 }\r
8819 \r
8820 \r
8821 int\r
8822 ClockTimerRunning()\r
8823 {\r
8824   return clockTimerEvent != 0;\r
8825 }\r
8826 \r
8827 int\r
8828 StopClockTimer()\r
8829 {\r
8830   if (clockTimerEvent == 0) return FALSE;\r
8831   KillTimer(hwndMain, clockTimerEvent);\r
8832   clockTimerEvent = 0;\r
8833   return TRUE;\r
8834 }\r
8835 \r
8836 void\r
8837 StartClockTimer(long millisec)\r
8838 {\r
8839   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8840                              (UINT) millisec, NULL);\r
8841 }\r
8842 \r
8843 void\r
8844 DisplayWhiteClock(long timeRemaining, int highlight)\r
8845 {\r
8846   HDC hdc;\r
8847   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8848 \r
8849   if(appData.noGUI) return;\r
8850   hdc = GetDC(hwndMain);\r
8851   if (!IsIconic(hwndMain)) {\r
8852     DisplayAClock(hdc, timeRemaining, highlight, \r
8853                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8854   }\r
8855   if (highlight && iconCurrent == iconBlack) {\r
8856     iconCurrent = iconWhite;\r
8857     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8858     if (IsIconic(hwndMain)) {\r
8859       DrawIcon(hdc, 2, 2, iconCurrent);\r
8860     }\r
8861   }\r
8862   (void) ReleaseDC(hwndMain, hdc);\r
8863   if (hwndConsole)\r
8864     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8865 }\r
8866 \r
8867 void\r
8868 DisplayBlackClock(long timeRemaining, int highlight)\r
8869 {\r
8870   HDC hdc;\r
8871   char *flag = blackFlag && 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 ? &whiteRect : &blackRect, _("Black"), flag);\r
8878   }\r
8879   if (highlight && iconCurrent == iconWhite) {\r
8880     iconCurrent = iconBlack;\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 \r
8892 int\r
8893 LoadGameTimerRunning()\r
8894 {\r
8895   return loadGameTimerEvent != 0;\r
8896 }\r
8897 \r
8898 int\r
8899 StopLoadGameTimer()\r
8900 {\r
8901   if (loadGameTimerEvent == 0) return FALSE;\r
8902   KillTimer(hwndMain, loadGameTimerEvent);\r
8903   loadGameTimerEvent = 0;\r
8904   return TRUE;\r
8905 }\r
8906 \r
8907 void\r
8908 StartLoadGameTimer(long millisec)\r
8909 {\r
8910   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8911                                 (UINT) millisec, NULL);\r
8912 }\r
8913 \r
8914 void\r
8915 AutoSaveGame()\r
8916 {\r
8917   char *defName;\r
8918   FILE *f;\r
8919   char fileTitle[MSG_SIZ];\r
8920 \r
8921   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8922   f = OpenFileDialog(hwndMain, "a", defName,\r
8923                      appData.oldSaveStyle ? "gam" : "pgn",\r
8924                      GAME_FILT, \r
8925                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8926   if (f != NULL) {\r
8927     SaveGame(f, 0, "");\r
8928     fclose(f);\r
8929   }\r
8930 }\r
8931 \r
8932 \r
8933 void\r
8934 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8935 {\r
8936   if (delayedTimerEvent != 0) {\r
8937     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8938       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8939     }\r
8940     KillTimer(hwndMain, delayedTimerEvent);\r
8941     delayedTimerEvent = 0;\r
8942     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8943     delayedTimerCallback();\r
8944   }\r
8945   delayedTimerCallback = cb;\r
8946   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8947                                 (UINT) millisec, NULL);\r
8948 }\r
8949 \r
8950 DelayedEventCallback\r
8951 GetDelayedEvent()\r
8952 {\r
8953   if (delayedTimerEvent) {\r
8954     return delayedTimerCallback;\r
8955   } else {\r
8956     return NULL;\r
8957   }\r
8958 }\r
8959 \r
8960 void\r
8961 CancelDelayedEvent()\r
8962 {\r
8963   if (delayedTimerEvent) {\r
8964     KillTimer(hwndMain, delayedTimerEvent);\r
8965     delayedTimerEvent = 0;\r
8966   }\r
8967 }\r
8968 \r
8969 DWORD GetWin32Priority(int nice)\r
8970 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8971 /*\r
8972 REALTIME_PRIORITY_CLASS     0x00000100\r
8973 HIGH_PRIORITY_CLASS         0x00000080\r
8974 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8975 NORMAL_PRIORITY_CLASS       0x00000020\r
8976 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8977 IDLE_PRIORITY_CLASS         0x00000040\r
8978 */\r
8979         if (nice < -15) return 0x00000080;\r
8980         if (nice < 0)   return 0x00008000;\r
8981         if (nice == 0)  return 0x00000020;\r
8982         if (nice < 15)  return 0x00004000;\r
8983         return 0x00000040;\r
8984 }\r
8985 \r
8986 void RunCommand(char *cmdLine)\r
8987 {\r
8988   /* Now create the child process. */\r
8989   STARTUPINFO siStartInfo;\r
8990   PROCESS_INFORMATION piProcInfo;\r
8991 \r
8992   siStartInfo.cb = sizeof(STARTUPINFO);\r
8993   siStartInfo.lpReserved = NULL;\r
8994   siStartInfo.lpDesktop = NULL;\r
8995   siStartInfo.lpTitle = NULL;\r
8996   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8997   siStartInfo.cbReserved2 = 0;\r
8998   siStartInfo.lpReserved2 = NULL;\r
8999   siStartInfo.hStdInput = NULL;\r
9000   siStartInfo.hStdOutput = NULL;\r
9001   siStartInfo.hStdError = NULL;\r
9002 \r
9003   CreateProcess(NULL,\r
9004                 cmdLine,           /* command line */\r
9005                 NULL,      /* process security attributes */\r
9006                 NULL,      /* primary thread security attrs */\r
9007                 TRUE,      /* handles are inherited */\r
9008                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9009                 NULL,      /* use parent's environment */\r
9010                 NULL,\r
9011                 &siStartInfo, /* STARTUPINFO pointer */\r
9012                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9013 \r
9014   CloseHandle(piProcInfo.hThread);\r
9015 }\r
9016 \r
9017 /* Start a child process running the given program.\r
9018    The process's standard output can be read from "from", and its\r
9019    standard input can be written to "to".\r
9020    Exit with fatal error if anything goes wrong.\r
9021    Returns an opaque pointer that can be used to destroy the process\r
9022    later.\r
9023 */\r
9024 int\r
9025 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9026 {\r
9027 #define BUFSIZE 4096\r
9028 \r
9029   HANDLE hChildStdinRd, hChildStdinWr,\r
9030     hChildStdoutRd, hChildStdoutWr;\r
9031   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9032   SECURITY_ATTRIBUTES saAttr;\r
9033   BOOL fSuccess;\r
9034   PROCESS_INFORMATION piProcInfo;\r
9035   STARTUPINFO siStartInfo;\r
9036   ChildProc *cp;\r
9037   char buf[MSG_SIZ];\r
9038   DWORD err;\r
9039 \r
9040   if (appData.debugMode) {\r
9041     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9042   }\r
9043 \r
9044   *pr = NoProc;\r
9045 \r
9046   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9047   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9048   saAttr.bInheritHandle = TRUE;\r
9049   saAttr.lpSecurityDescriptor = NULL;\r
9050 \r
9051   /*\r
9052    * The steps for redirecting child's STDOUT:\r
9053    *     1. Create anonymous pipe to be STDOUT for child.\r
9054    *     2. Create a noninheritable duplicate of read handle,\r
9055    *         and close the inheritable read handle.\r
9056    */\r
9057 \r
9058   /* Create a pipe for the child's STDOUT. */\r
9059   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9060     return GetLastError();\r
9061   }\r
9062 \r
9063   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9064   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9065                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9066                              FALSE,     /* not inherited */\r
9067                              DUPLICATE_SAME_ACCESS);\r
9068   if (! fSuccess) {\r
9069     return GetLastError();\r
9070   }\r
9071   CloseHandle(hChildStdoutRd);\r
9072 \r
9073   /*\r
9074    * The steps for redirecting child's STDIN:\r
9075    *     1. Create anonymous pipe to be STDIN for child.\r
9076    *     2. Create a noninheritable duplicate of write handle,\r
9077    *         and close the inheritable write handle.\r
9078    */\r
9079 \r
9080   /* Create a pipe for the child's STDIN. */\r
9081   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9082     return GetLastError();\r
9083   }\r
9084 \r
9085   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9086   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9087                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9088                              FALSE,     /* not inherited */\r
9089                              DUPLICATE_SAME_ACCESS);\r
9090   if (! fSuccess) {\r
9091     return GetLastError();\r
9092   }\r
9093   CloseHandle(hChildStdinWr);\r
9094 \r
9095   /* Arrange to (1) look in dir for the child .exe file, and\r
9096    * (2) have dir be the child's working directory.  Interpret\r
9097    * dir relative to the directory WinBoard loaded from. */\r
9098   GetCurrentDirectory(MSG_SIZ, buf);\r
9099   SetCurrentDirectory(installDir);\r
9100   SetCurrentDirectory(dir);\r
9101 \r
9102   /* Now create the child process. */\r
9103 \r
9104   siStartInfo.cb = sizeof(STARTUPINFO);\r
9105   siStartInfo.lpReserved = NULL;\r
9106   siStartInfo.lpDesktop = NULL;\r
9107   siStartInfo.lpTitle = NULL;\r
9108   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9109   siStartInfo.cbReserved2 = 0;\r
9110   siStartInfo.lpReserved2 = NULL;\r
9111   siStartInfo.hStdInput = hChildStdinRd;\r
9112   siStartInfo.hStdOutput = hChildStdoutWr;\r
9113   siStartInfo.hStdError = hChildStdoutWr;\r
9114 \r
9115   fSuccess = CreateProcess(NULL,\r
9116                            cmdLine,        /* command line */\r
9117                            NULL,           /* process security attributes */\r
9118                            NULL,           /* primary thread security attrs */\r
9119                            TRUE,           /* handles are inherited */\r
9120                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9121                            NULL,           /* use parent's environment */\r
9122                            NULL,\r
9123                            &siStartInfo, /* STARTUPINFO pointer */\r
9124                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9125 \r
9126   err = GetLastError();\r
9127   SetCurrentDirectory(buf); /* return to prev directory */\r
9128   if (! fSuccess) {\r
9129     return err;\r
9130   }\r
9131 \r
9132   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9133     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9134     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9135   }\r
9136 \r
9137   /* Close the handles we don't need in the parent */\r
9138   CloseHandle(piProcInfo.hThread);\r
9139   CloseHandle(hChildStdinRd);\r
9140   CloseHandle(hChildStdoutWr);\r
9141 \r
9142   /* Prepare return value */\r
9143   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9144   cp->kind = CPReal;\r
9145   cp->hProcess = piProcInfo.hProcess;\r
9146   cp->pid = piProcInfo.dwProcessId;\r
9147   cp->hFrom = hChildStdoutRdDup;\r
9148   cp->hTo = hChildStdinWrDup;\r
9149 \r
9150   *pr = (void *) cp;\r
9151 \r
9152   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9153      2000 where engines sometimes don't see the initial command(s)\r
9154      from WinBoard and hang.  I don't understand how that can happen,\r
9155      but the Sleep is harmless, so I've put it in.  Others have also\r
9156      reported what may be the same problem, so hopefully this will fix\r
9157      it for them too.  */\r
9158   Sleep(500);\r
9159 \r
9160   return NO_ERROR;\r
9161 }\r
9162 \r
9163 \r
9164 void\r
9165 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9166 {\r
9167   ChildProc *cp; int result;\r
9168 \r
9169   cp = (ChildProc *) pr;\r
9170   if (cp == NULL) return;\r
9171 \r
9172   switch (cp->kind) {\r
9173   case CPReal:\r
9174     /* TerminateProcess is considered harmful, so... */\r
9175     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9176     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9177     /* The following doesn't work because the chess program\r
9178        doesn't "have the same console" as WinBoard.  Maybe\r
9179        we could arrange for this even though neither WinBoard\r
9180        nor the chess program uses a console for stdio? */\r
9181     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9182 \r
9183     /* [AS] Special termination modes for misbehaving programs... */\r
9184     if( signal == 9 ) { \r
9185         result = TerminateProcess( cp->hProcess, 0 );\r
9186 \r
9187         if ( appData.debugMode) {\r
9188             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9189         }\r
9190     }\r
9191     else if( signal == 10 ) {\r
9192         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9193 \r
9194         if( dw != WAIT_OBJECT_0 ) {\r
9195             result = TerminateProcess( cp->hProcess, 0 );\r
9196 \r
9197             if ( appData.debugMode) {\r
9198                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9199             }\r
9200 \r
9201         }\r
9202     }\r
9203 \r
9204     CloseHandle(cp->hProcess);\r
9205     break;\r
9206 \r
9207   case CPComm:\r
9208     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9209     break;\r
9210 \r
9211   case CPSock:\r
9212     closesocket(cp->sock);\r
9213     WSACleanup();\r
9214     break;\r
9215 \r
9216   case CPRcmd:\r
9217     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9218     closesocket(cp->sock);\r
9219     closesocket(cp->sock2);\r
9220     WSACleanup();\r
9221     break;\r
9222   }\r
9223   free(cp);\r
9224 }\r
9225 \r
9226 void\r
9227 InterruptChildProcess(ProcRef pr)\r
9228 {\r
9229   ChildProc *cp;\r
9230 \r
9231   cp = (ChildProc *) pr;\r
9232   if (cp == NULL) return;\r
9233   switch (cp->kind) {\r
9234   case CPReal:\r
9235     /* The following doesn't work because the chess program\r
9236        doesn't "have the same console" as WinBoard.  Maybe\r
9237        we could arrange for this even though neither WinBoard\r
9238        nor the chess program uses a console for stdio */\r
9239     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9240     break;\r
9241 \r
9242   case CPComm:\r
9243   case CPSock:\r
9244     /* Can't interrupt */\r
9245     break;\r
9246 \r
9247   case CPRcmd:\r
9248     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9249     break;\r
9250   }\r
9251 }\r
9252 \r
9253 \r
9254 int\r
9255 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9256 {\r
9257   char cmdLine[MSG_SIZ];\r
9258 \r
9259   if (port[0] == NULLCHAR) {\r
9260     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9261   } else {\r
9262     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9263   }\r
9264   return StartChildProcess(cmdLine, "", pr);\r
9265 }\r
9266 \r
9267 \r
9268 /* Code to open TCP sockets */\r
9269 \r
9270 int\r
9271 OpenTCP(char *host, char *port, ProcRef *pr)\r
9272 {\r
9273   ChildProc *cp;\r
9274   int err;\r
9275   SOCKET s;\r
9276 \r
9277   struct sockaddr_in sa, mysa;\r
9278   struct hostent FAR *hp;\r
9279   unsigned short uport;\r
9280   WORD wVersionRequested;\r
9281   WSADATA wsaData;\r
9282 \r
9283   /* Initialize socket DLL */\r
9284   wVersionRequested = MAKEWORD(1, 1);\r
9285   err = WSAStartup(wVersionRequested, &wsaData);\r
9286   if (err != 0) return err;\r
9287 \r
9288   /* Make socket */\r
9289   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9290     err = WSAGetLastError();\r
9291     WSACleanup();\r
9292     return err;\r
9293   }\r
9294 \r
9295   /* Bind local address using (mostly) don't-care values.\r
9296    */\r
9297   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9298   mysa.sin_family = AF_INET;\r
9299   mysa.sin_addr.s_addr = INADDR_ANY;\r
9300   uport = (unsigned short) 0;\r
9301   mysa.sin_port = htons(uport);\r
9302   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9303       == SOCKET_ERROR) {\r
9304     err = WSAGetLastError();\r
9305     WSACleanup();\r
9306     return err;\r
9307   }\r
9308 \r
9309   /* Resolve remote host name */\r
9310   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9311   if (!(hp = gethostbyname(host))) {\r
9312     unsigned int b0, b1, b2, b3;\r
9313 \r
9314     err = WSAGetLastError();\r
9315 \r
9316     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9317       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9318       hp->h_addrtype = AF_INET;\r
9319       hp->h_length = 4;\r
9320       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9321       hp->h_addr_list[0] = (char *) malloc(4);\r
9322       hp->h_addr_list[0][0] = (char) b0;\r
9323       hp->h_addr_list[0][1] = (char) b1;\r
9324       hp->h_addr_list[0][2] = (char) b2;\r
9325       hp->h_addr_list[0][3] = (char) b3;\r
9326     } else {\r
9327       WSACleanup();\r
9328       return err;\r
9329     }\r
9330   }\r
9331   sa.sin_family = hp->h_addrtype;\r
9332   uport = (unsigned short) atoi(port);\r
9333   sa.sin_port = htons(uport);\r
9334   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9335 \r
9336   /* Make connection */\r
9337   if (connect(s, (struct sockaddr *) &sa,\r
9338               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9339     err = WSAGetLastError();\r
9340     WSACleanup();\r
9341     return err;\r
9342   }\r
9343 \r
9344   /* Prepare return value */\r
9345   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9346   cp->kind = CPSock;\r
9347   cp->sock = s;\r
9348   *pr = (ProcRef *) cp;\r
9349 \r
9350   return NO_ERROR;\r
9351 }\r
9352 \r
9353 int\r
9354 OpenCommPort(char *name, ProcRef *pr)\r
9355 {\r
9356   HANDLE h;\r
9357   COMMTIMEOUTS ct;\r
9358   ChildProc *cp;\r
9359   char fullname[MSG_SIZ];\r
9360 \r
9361   if (*name != '\\')\r
9362     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9363   else\r
9364     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9365 \r
9366   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9367                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9368   if (h == (HANDLE) -1) {\r
9369     return GetLastError();\r
9370   }\r
9371   hCommPort = h;\r
9372 \r
9373   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9374 \r
9375   /* Accumulate characters until a 100ms pause, then parse */\r
9376   ct.ReadIntervalTimeout = 100;\r
9377   ct.ReadTotalTimeoutMultiplier = 0;\r
9378   ct.ReadTotalTimeoutConstant = 0;\r
9379   ct.WriteTotalTimeoutMultiplier = 0;\r
9380   ct.WriteTotalTimeoutConstant = 0;\r
9381   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9382 \r
9383   /* Prepare return value */\r
9384   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9385   cp->kind = CPComm;\r
9386   cp->hFrom = h;\r
9387   cp->hTo = h;\r
9388   *pr = (ProcRef *) cp;\r
9389 \r
9390   return NO_ERROR;\r
9391 }\r
9392 \r
9393 int\r
9394 OpenLoopback(ProcRef *pr)\r
9395 {\r
9396   DisplayFatalError(_("Not implemented"), 0, 1);\r
9397   return NO_ERROR;\r
9398 }\r
9399 \r
9400 \r
9401 int\r
9402 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9403 {\r
9404   ChildProc *cp;\r
9405   int err;\r
9406   SOCKET s, s2, s3;\r
9407   struct sockaddr_in sa, mysa;\r
9408   struct hostent FAR *hp;\r
9409   unsigned short uport;\r
9410   WORD wVersionRequested;\r
9411   WSADATA wsaData;\r
9412   int fromPort;\r
9413   char stderrPortStr[MSG_SIZ];\r
9414 \r
9415   /* Initialize socket DLL */\r
9416   wVersionRequested = MAKEWORD(1, 1);\r
9417   err = WSAStartup(wVersionRequested, &wsaData);\r
9418   if (err != 0) return err;\r
9419 \r
9420   /* Resolve remote host name */\r
9421   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9422   if (!(hp = gethostbyname(host))) {\r
9423     unsigned int b0, b1, b2, b3;\r
9424 \r
9425     err = WSAGetLastError();\r
9426 \r
9427     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9428       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9429       hp->h_addrtype = AF_INET;\r
9430       hp->h_length = 4;\r
9431       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9432       hp->h_addr_list[0] = (char *) malloc(4);\r
9433       hp->h_addr_list[0][0] = (char) b0;\r
9434       hp->h_addr_list[0][1] = (char) b1;\r
9435       hp->h_addr_list[0][2] = (char) b2;\r
9436       hp->h_addr_list[0][3] = (char) b3;\r
9437     } else {\r
9438       WSACleanup();\r
9439       return err;\r
9440     }\r
9441   }\r
9442   sa.sin_family = hp->h_addrtype;\r
9443   uport = (unsigned short) 514;\r
9444   sa.sin_port = htons(uport);\r
9445   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9446 \r
9447   /* Bind local socket to unused "privileged" port address\r
9448    */\r
9449   s = INVALID_SOCKET;\r
9450   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9451   mysa.sin_family = AF_INET;\r
9452   mysa.sin_addr.s_addr = INADDR_ANY;\r
9453   for (fromPort = 1023;; fromPort--) {\r
9454     if (fromPort < 0) {\r
9455       WSACleanup();\r
9456       return WSAEADDRINUSE;\r
9457     }\r
9458     if (s == INVALID_SOCKET) {\r
9459       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9460         err = WSAGetLastError();\r
9461         WSACleanup();\r
9462         return err;\r
9463       }\r
9464     }\r
9465     uport = (unsigned short) fromPort;\r
9466     mysa.sin_port = htons(uport);\r
9467     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9468         == SOCKET_ERROR) {\r
9469       err = WSAGetLastError();\r
9470       if (err == WSAEADDRINUSE) continue;\r
9471       WSACleanup();\r
9472       return err;\r
9473     }\r
9474     if (connect(s, (struct sockaddr *) &sa,\r
9475       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9476       err = WSAGetLastError();\r
9477       if (err == WSAEADDRINUSE) {\r
9478         closesocket(s);\r
9479         s = -1;\r
9480         continue;\r
9481       }\r
9482       WSACleanup();\r
9483       return err;\r
9484     }\r
9485     break;\r
9486   }\r
9487 \r
9488   /* Bind stderr local socket to unused "privileged" port address\r
9489    */\r
9490   s2 = INVALID_SOCKET;\r
9491   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9492   mysa.sin_family = AF_INET;\r
9493   mysa.sin_addr.s_addr = INADDR_ANY;\r
9494   for (fromPort = 1023;; fromPort--) {\r
9495     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9496     if (fromPort < 0) {\r
9497       (void) closesocket(s);\r
9498       WSACleanup();\r
9499       return WSAEADDRINUSE;\r
9500     }\r
9501     if (s2 == INVALID_SOCKET) {\r
9502       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9503         err = WSAGetLastError();\r
9504         closesocket(s);\r
9505         WSACleanup();\r
9506         return err;\r
9507       }\r
9508     }\r
9509     uport = (unsigned short) fromPort;\r
9510     mysa.sin_port = htons(uport);\r
9511     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9512         == SOCKET_ERROR) {\r
9513       err = WSAGetLastError();\r
9514       if (err == WSAEADDRINUSE) continue;\r
9515       (void) closesocket(s);\r
9516       WSACleanup();\r
9517       return err;\r
9518     }\r
9519     if (listen(s2, 1) == SOCKET_ERROR) {\r
9520       err = WSAGetLastError();\r
9521       if (err == WSAEADDRINUSE) {\r
9522         closesocket(s2);\r
9523         s2 = INVALID_SOCKET;\r
9524         continue;\r
9525       }\r
9526       (void) closesocket(s);\r
9527       (void) closesocket(s2);\r
9528       WSACleanup();\r
9529       return err;\r
9530     }\r
9531     break;\r
9532   }\r
9533   prevStderrPort = fromPort; // remember port used\r
9534   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9535 \r
9536   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9537     err = WSAGetLastError();\r
9538     (void) closesocket(s);\r
9539     (void) closesocket(s2);\r
9540     WSACleanup();\r
9541     return err;\r
9542   }\r
9543 \r
9544   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9545     err = WSAGetLastError();\r
9546     (void) closesocket(s);\r
9547     (void) closesocket(s2);\r
9548     WSACleanup();\r
9549     return err;\r
9550   }\r
9551   if (*user == NULLCHAR) user = UserName();\r
9552   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9553     err = WSAGetLastError();\r
9554     (void) closesocket(s);\r
9555     (void) closesocket(s2);\r
9556     WSACleanup();\r
9557     return err;\r
9558   }\r
9559   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9560     err = WSAGetLastError();\r
9561     (void) closesocket(s);\r
9562     (void) closesocket(s2);\r
9563     WSACleanup();\r
9564     return err;\r
9565   }\r
9566 \r
9567   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9568     err = WSAGetLastError();\r
9569     (void) closesocket(s);\r
9570     (void) closesocket(s2);\r
9571     WSACleanup();\r
9572     return err;\r
9573   }\r
9574   (void) closesocket(s2);  /* Stop listening */\r
9575 \r
9576   /* Prepare return value */\r
9577   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9578   cp->kind = CPRcmd;\r
9579   cp->sock = s;\r
9580   cp->sock2 = s3;\r
9581   *pr = (ProcRef *) cp;\r
9582 \r
9583   return NO_ERROR;\r
9584 }\r
9585 \r
9586 \r
9587 InputSourceRef\r
9588 AddInputSource(ProcRef pr, int lineByLine,\r
9589                InputCallback func, VOIDSTAR closure)\r
9590 {\r
9591   InputSource *is, *is2 = NULL;\r
9592   ChildProc *cp = (ChildProc *) pr;\r
9593 \r
9594   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9595   is->lineByLine = lineByLine;\r
9596   is->func = func;\r
9597   is->closure = closure;\r
9598   is->second = NULL;\r
9599   is->next = is->buf;\r
9600   if (pr == NoProc) {\r
9601     is->kind = CPReal;\r
9602     consoleInputSource = is;\r
9603   } else {\r
9604     is->kind = cp->kind;\r
9605     /* \r
9606         [AS] Try to avoid a race condition if the thread is given control too early:\r
9607         we create all threads suspended so that the is->hThread variable can be\r
9608         safely assigned, then let the threads start with ResumeThread.\r
9609     */\r
9610     switch (cp->kind) {\r
9611     case CPReal:\r
9612       is->hFile = cp->hFrom;\r
9613       cp->hFrom = NULL; /* now owned by InputThread */\r
9614       is->hThread =\r
9615         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9616                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9617       break;\r
9618 \r
9619     case CPComm:\r
9620       is->hFile = cp->hFrom;\r
9621       cp->hFrom = NULL; /* now owned by InputThread */\r
9622       is->hThread =\r
9623         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9624                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9625       break;\r
9626 \r
9627     case CPSock:\r
9628       is->sock = cp->sock;\r
9629       is->hThread =\r
9630         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9631                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9632       break;\r
9633 \r
9634     case CPRcmd:\r
9635       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9636       *is2 = *is;\r
9637       is->sock = cp->sock;\r
9638       is->second = is2;\r
9639       is2->sock = cp->sock2;\r
9640       is2->second = is2;\r
9641       is->hThread =\r
9642         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9643                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9644       is2->hThread =\r
9645         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9646                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9647       break;\r
9648     }\r
9649 \r
9650     if( is->hThread != NULL ) {\r
9651         ResumeThread( is->hThread );\r
9652     }\r
9653 \r
9654     if( is2 != NULL && is2->hThread != NULL ) {\r
9655         ResumeThread( is2->hThread );\r
9656     }\r
9657   }\r
9658 \r
9659   return (InputSourceRef) is;\r
9660 }\r
9661 \r
9662 void\r
9663 RemoveInputSource(InputSourceRef isr)\r
9664 {\r
9665   InputSource *is;\r
9666 \r
9667   is = (InputSource *) isr;\r
9668   is->hThread = NULL;  /* tell thread to stop */\r
9669   CloseHandle(is->hThread);\r
9670   if (is->second != NULL) {\r
9671     is->second->hThread = NULL;\r
9672     CloseHandle(is->second->hThread);\r
9673   }\r
9674 }\r
9675 \r
9676 int no_wrap(char *message, int count)\r
9677 {\r
9678     ConsoleOutput(message, count, FALSE);\r
9679     return count;\r
9680 }\r
9681 \r
9682 int\r
9683 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9684 {\r
9685   DWORD dOutCount;\r
9686   int outCount = SOCKET_ERROR;\r
9687   ChildProc *cp = (ChildProc *) pr;\r
9688   static OVERLAPPED ovl;\r
9689   static int line = 0;\r
9690 \r
9691   if (pr == NoProc)\r
9692   {\r
9693     if (appData.noJoin || !appData.useInternalWrap)\r
9694       return no_wrap(message, count);\r
9695     else\r
9696     {\r
9697       int width = get_term_width();\r
9698       int len = wrap(NULL, message, count, width, &line);\r
9699       char *msg = malloc(len);\r
9700       int dbgchk;\r
9701 \r
9702       if (!msg)\r
9703         return no_wrap(message, count);\r
9704       else\r
9705       {\r
9706         dbgchk = wrap(msg, message, count, width, &line);\r
9707         if (dbgchk != len && appData.debugMode)\r
9708             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9709         ConsoleOutput(msg, len, FALSE);\r
9710         free(msg);\r
9711         return len;\r
9712       }\r
9713     }\r
9714   }\r
9715 \r
9716   if (ovl.hEvent == NULL) {\r
9717     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9718   }\r
9719   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9720 \r
9721   switch (cp->kind) {\r
9722   case CPSock:\r
9723   case CPRcmd:\r
9724     outCount = send(cp->sock, message, count, 0);\r
9725     if (outCount == SOCKET_ERROR) {\r
9726       *outError = WSAGetLastError();\r
9727     } else {\r
9728       *outError = NO_ERROR;\r
9729     }\r
9730     break;\r
9731 \r
9732   case CPReal:\r
9733     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9734                   &dOutCount, NULL)) {\r
9735       *outError = NO_ERROR;\r
9736       outCount = (int) dOutCount;\r
9737     } else {\r
9738       *outError = GetLastError();\r
9739     }\r
9740     break;\r
9741 \r
9742   case CPComm:\r
9743     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9744                             &dOutCount, &ovl);\r
9745     if (*outError == NO_ERROR) {\r
9746       outCount = (int) dOutCount;\r
9747     }\r
9748     break;\r
9749   }\r
9750   return outCount;\r
9751 }\r
9752 \r
9753 void\r
9754 DoSleep(int n)\r
9755 {\r
9756     if(n != 0) Sleep(n);\r
9757 }\r
9758 \r
9759 int\r
9760 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9761                        long msdelay)\r
9762 {\r
9763   /* Ignore delay, not implemented for WinBoard */\r
9764   return OutputToProcess(pr, message, count, outError);\r
9765 }\r
9766 \r
9767 \r
9768 void\r
9769 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9770                         char *buf, int count, int error)\r
9771 {\r
9772   DisplayFatalError(_("Not implemented"), 0, 1);\r
9773 }\r
9774 \r
9775 /* see wgamelist.c for Game List functions */\r
9776 /* see wedittags.c for Edit Tags functions */\r
9777 \r
9778 \r
9779 int\r
9780 ICSInitScript()\r
9781 {\r
9782   FILE *f;\r
9783   char buf[MSG_SIZ];\r
9784   char *dummy;\r
9785 \r
9786   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9787     f = fopen(buf, "r");\r
9788     if (f != NULL) {\r
9789       ProcessICSInitScript(f);\r
9790       fclose(f);\r
9791       return TRUE;\r
9792     }\r
9793   }\r
9794   return FALSE;\r
9795 }\r
9796 \r
9797 \r
9798 VOID\r
9799 StartAnalysisClock()\r
9800 {\r
9801   if (analysisTimerEvent) return;\r
9802   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9803                                         (UINT) 2000, NULL);\r
9804 }\r
9805 \r
9806 VOID\r
9807 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9808 {\r
9809   highlightInfo.sq[0].x = fromX;\r
9810   highlightInfo.sq[0].y = fromY;\r
9811   highlightInfo.sq[1].x = toX;\r
9812   highlightInfo.sq[1].y = toY;\r
9813 }\r
9814 \r
9815 VOID\r
9816 ClearHighlights()\r
9817 {\r
9818   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9819     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9820 }\r
9821 \r
9822 VOID\r
9823 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9824 {\r
9825   premoveHighlightInfo.sq[0].x = fromX;\r
9826   premoveHighlightInfo.sq[0].y = fromY;\r
9827   premoveHighlightInfo.sq[1].x = toX;\r
9828   premoveHighlightInfo.sq[1].y = toY;\r
9829 }\r
9830 \r
9831 VOID\r
9832 ClearPremoveHighlights()\r
9833 {\r
9834   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9835     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9836 }\r
9837 \r
9838 VOID\r
9839 ShutDownFrontEnd()\r
9840 {\r
9841   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9842   DeleteClipboardTempFiles();\r
9843 }\r
9844 \r
9845 void\r
9846 BoardToTop()\r
9847 {\r
9848     if (IsIconic(hwndMain))\r
9849       ShowWindow(hwndMain, SW_RESTORE);\r
9850 \r
9851     SetActiveWindow(hwndMain);\r
9852 }\r
9853 \r
9854 /*\r
9855  * Prototypes for animation support routines\r
9856  */\r
9857 static void ScreenSquare(int column, int row, POINT * pt);\r
9858 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9859      POINT frames[], int * nFrames);\r
9860 \r
9861 \r
9862 #define kFactor 4\r
9863 \r
9864 void\r
9865 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9866 {       // [HGM] atomic: animate blast wave\r
9867         int i;\r
9868 \r
9869         explodeInfo.fromX = fromX;\r
9870         explodeInfo.fromY = fromY;\r
9871         explodeInfo.toX = toX;\r
9872         explodeInfo.toY = toY;\r
9873         for(i=1; i<4*kFactor; i++) {\r
9874             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9875             DrawPosition(FALSE, board);\r
9876             Sleep(appData.animSpeed);\r
9877         }\r
9878         explodeInfo.radius = 0;\r
9879         DrawPosition(TRUE, board);\r
9880 }\r
9881 \r
9882 void\r
9883 AnimateMove(board, fromX, fromY, toX, toY)\r
9884      Board board;\r
9885      int fromX;\r
9886      int fromY;\r
9887      int toX;\r
9888      int toY;\r
9889 {\r
9890   ChessSquare piece;\r
9891   POINT start, finish, mid;\r
9892   POINT frames[kFactor * 2 + 1];\r
9893   int nFrames, n;\r
9894 \r
9895   if (!appData.animate) return;\r
9896   if (doingSizing) return;\r
9897   if (fromY < 0 || fromX < 0) return;\r
9898   piece = board[fromY][fromX];\r
9899   if (piece >= EmptySquare) return;\r
9900 \r
9901   ScreenSquare(fromX, fromY, &start);\r
9902   ScreenSquare(toX, toY, &finish);\r
9903 \r
9904   /* All moves except knight jumps move in straight line */\r
9905   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9906     mid.x = start.x + (finish.x - start.x) / 2;\r
9907     mid.y = start.y + (finish.y - start.y) / 2;\r
9908   } else {\r
9909     /* Knight: make straight movement then diagonal */\r
9910     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9911        mid.x = start.x + (finish.x - start.x) / 2;\r
9912        mid.y = start.y;\r
9913      } else {\r
9914        mid.x = start.x;\r
9915        mid.y = start.y + (finish.y - start.y) / 2;\r
9916      }\r
9917   }\r
9918   \r
9919   /* Don't use as many frames for very short moves */\r
9920   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9921     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9922   else\r
9923     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9924 \r
9925   animInfo.from.x = fromX;\r
9926   animInfo.from.y = fromY;\r
9927   animInfo.to.x = toX;\r
9928   animInfo.to.y = toY;\r
9929   animInfo.lastpos = start;\r
9930   animInfo.piece = piece;\r
9931   for (n = 0; n < nFrames; n++) {\r
9932     animInfo.pos = frames[n];\r
9933     DrawPosition(FALSE, NULL);\r
9934     animInfo.lastpos = animInfo.pos;\r
9935     Sleep(appData.animSpeed);\r
9936   }\r
9937   animInfo.pos = finish;\r
9938   DrawPosition(FALSE, NULL);\r
9939   animInfo.piece = EmptySquare;\r
9940   Explode(board, fromX, fromY, toX, toY);\r
9941 }\r
9942 \r
9943 /*      Convert board position to corner of screen rect and color       */\r
9944 \r
9945 static void\r
9946 ScreenSquare(column, row, pt)\r
9947      int column; int row; POINT * pt;\r
9948 {\r
9949   if (flipView) {\r
9950     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9951     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9952   } else {\r
9953     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9954     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9955   }\r
9956 }\r
9957 \r
9958 /*      Generate a series of frame coords from start->mid->finish.\r
9959         The movement rate doubles until the half way point is\r
9960         reached, then halves back down to the final destination,\r
9961         which gives a nice slow in/out effect. The algorithmn\r
9962         may seem to generate too many intermediates for short\r
9963         moves, but remember that the purpose is to attract the\r
9964         viewers attention to the piece about to be moved and\r
9965         then to where it ends up. Too few frames would be less\r
9966         noticeable.                                             */\r
9967 \r
9968 static void\r
9969 Tween(start, mid, finish, factor, frames, nFrames)\r
9970      POINT * start; POINT * mid;\r
9971      POINT * finish; int factor;\r
9972      POINT frames[]; int * nFrames;\r
9973 {\r
9974   int n, fraction = 1, count = 0;\r
9975 \r
9976   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9977   for (n = 0; n < factor; n++)\r
9978     fraction *= 2;\r
9979   for (n = 0; n < factor; n++) {\r
9980     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9981     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9982     count ++;\r
9983     fraction = fraction / 2;\r
9984   }\r
9985   \r
9986   /* Midpoint */\r
9987   frames[count] = *mid;\r
9988   count ++;\r
9989   \r
9990   /* Slow out, stepping 1/2, then 1/4, ... */\r
9991   fraction = 2;\r
9992   for (n = 0; n < factor; n++) {\r
9993     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9994     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9995     count ++;\r
9996     fraction = fraction * 2;\r
9997   }\r
9998   *nFrames = count;\r
9999 }\r
10000 \r
10001 void\r
10002 SettingsPopUp(ChessProgramState *cps)\r
10003 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10004       EngineOptionsPopup(savedHwnd, cps);\r
10005 }\r
10006 \r
10007 int flock(int fid, int code)\r
10008 {\r
10009     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10010     OVERLAPPED ov;\r
10011     ov.hEvent = NULL;\r
10012     ov.Offset = 0;\r
10013     ov.OffsetHigh = 0;\r
10014     switch(code) {\r
10015       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10016       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10017       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10018       default: return -1;\r
10019     }\r
10020     return 0;\r
10021 }\r
10022 \r
10023 char *\r
10024 Col2Text (int n)\r
10025 {\r
10026     static int i=0;\r
10027     static char col[8][20];\r
10028     COLORREF color = *(COLORREF *) colorVariable[n];\r
10029     i = i+1 & 7;\r
10030     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10031     return col[i];\r
10032 }\r
10033 \r
10034 void\r
10035 ActivateTheme (int new)\r
10036 {   // Redo initialization of features depending on options that can occur in themes\r
10037    InitTextures();\r
10038    if(new) InitDrawingColors();\r
10039    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10040    InitDrawingSizes(boardSize, 0);\r
10041    InvalidateRect(hwndMain, NULL, TRUE);\r
10042 }\r