2b1322284d6c287b6f4d776f1da17214a2d03d42
[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 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
191 HPALETTE hPal;\r
192 ColorClass currentColorClass;\r
193 \r
194 static HWND savedHwnd;\r
195 HWND hCommPort = NULL;    /* currently open comm port */\r
196 static HWND hwndPause;    /* pause button */\r
197 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
198 static HBRUSH lightSquareBrush, darkSquareBrush,\r
199   blackSquareBrush, /* [HGM] for band between board and holdings */\r
200   explodeBrush,     /* [HGM] atomic */\r
201   markerBrush[8],   /* [HGM] markers */\r
202   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
203 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
204 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
205 static HPEN gridPen = NULL;\r
206 static HPEN highlightPen = NULL;\r
207 static HPEN premovePen = NULL;\r
208 static NPLOGPALETTE pLogPal;\r
209 static BOOL paletteChanged = FALSE;\r
210 static HICON iconWhite, iconBlack, iconCurrent;\r
211 static int doingSizing = FALSE;\r
212 static int lastSizing = 0;\r
213 static int prevStderrPort;\r
214 static HBITMAP userLogo;\r
215 \r
216 static HBITMAP liteBackTexture = NULL;\r
217 static HBITMAP darkBackTexture = NULL;\r
218 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
220 static int backTextureSquareSize = 0;\r
221 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
222 \r
223 #if __GNUC__ && !defined(_winmajor)\r
224 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
225 #else\r
226 \r
227 #if defined(_winmajor)\r
228 #define oldDialog (_winmajor < 4)\r
229 #else\r
230 #define oldDialog 0\r
231 #endif\r
232 #endif\r
233 \r
234 #define INTERNATIONAL\r
235 \r
236 #ifdef INTERNATIONAL\r
237 #  define _(s) T_(s)\r
238 #  define N_(s) s\r
239 #else\r
240 #  define _(s) s\r
241 #  define N_(s) s\r
242 #  define T_(s) s\r
243 #  define Translate(x, y)\r
244 #  define LoadLanguageFile(s)\r
245 #endif\r
246 \r
247 #ifdef INTERNATIONAL\r
248 \r
249 Boolean barbaric; // flag indicating if translation is needed\r
250 \r
251 // list of item numbers used in each dialog (used to alter language at run time)\r
252 \r
253 #define ABOUTBOX -1  /* not sure why these are needed */\r
254 #define ABOUTBOX2 -1\r
255 \r
256 int dialogItems[][42] = {\r
257 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
258 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
259   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
260 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
261   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
262 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
263   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
264 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
265 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
266   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
267 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
268 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
269   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
270 { ABOUTBOX2, IDC_ChessBoard }, \r
271 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
272   OPT_GameListClose, IDC_GameListDoFilter }, \r
273 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
274 { DLG_Error, IDOK }, \r
275 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
276   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
277 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
278 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
279   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
280   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
281 { DLG_IndexNumber, IDC_Index }, \r
282 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
283 { DLG_TypeInName, IDOK, IDCANCEL }, \r
284 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
285   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
286 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
287   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
288   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
289   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
290   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
291   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
292   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
293 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
294   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
295   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
296   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
297   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
298   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
299   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
300   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
301   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
302 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
303   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
304   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
305   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
306   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
307   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
308   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
309   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
310 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
311   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
312   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
313   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
314   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
315   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
316   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
317   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
318   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
319 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
320   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
321   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
322   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
323   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
324 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
325 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
326   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
327 { DLG_MoveHistory }, \r
328 { DLG_EvalGraph }, \r
329 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
330 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
331 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
332   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
333   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
334   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
335 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
336   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
337   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
338 { 0 }\r
339 };\r
340 \r
341 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
342 static int lastChecked;\r
343 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
344 extern int tinyLayout;\r
345 extern char * menuBarText[][10];\r
346 \r
347 void\r
348 LoadLanguageFile(char *name)\r
349 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
350     FILE *f;\r
351     int i=0, j=0, n=0, k;\r
352     char buf[MSG_SIZ];\r
353 \r
354     if(!name || name[0] == NULLCHAR) return;\r
355       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
356     appData.language = oldLanguage;\r
357     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
358     if((f = fopen(buf, "r")) == NULL) return;\r
359     while((k = fgetc(f)) != EOF) {\r
360         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
361         languageBuf[i] = k;\r
362         if(k == '\n') {\r
363             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
364                 char *p;\r
365                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
366                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
367                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
368                         english[j] = languageBuf + n + 1; *p = 0;\r
369                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
370 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
371                     }\r
372                 }\r
373             }\r
374             n = i + 1;\r
375         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
376             switch(k) {\r
377               case 'n': k = '\n'; break;\r
378               case 'r': k = '\r'; break;\r
379               case 't': k = '\t'; break;\r
380             }\r
381             languageBuf[--i] = k;\r
382         }\r
383         i++;\r
384     }\r
385     fclose(f);\r
386     barbaric = (j != 0);\r
387     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
388 }\r
389 \r
390 char *\r
391 T_(char *s)\r
392 {   // return the translation of the given string\r
393     // efficiency can be improved a lot...\r
394     int i=0;\r
395     static char buf[MSG_SIZ];\r
396 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
397     if(!barbaric) return s;\r
398     if(!s) return ""; // sanity\r
399     while(english[i]) {\r
400         if(!strcmp(s, english[i])) return foreign[i];\r
401         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
402             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
403             return buf;\r
404         }\r
405         i++;\r
406     }\r
407     return s;\r
408 }\r
409 \r
410 void\r
411 Translate(HWND hDlg, int dialogID)\r
412 {   // translate all text items in the given dialog\r
413     int i=0, j, k;\r
414     char buf[MSG_SIZ], *s;\r
415     if(!barbaric) return;\r
416     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
417     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
418     GetWindowText( hDlg, buf, MSG_SIZ );\r
419     s = T_(buf);\r
420     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
421     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
422         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
423         if(strlen(buf) == 0) continue;\r
424         s = T_(buf);\r
425         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
426     }\r
427 }\r
428 \r
429 HMENU\r
430 TranslateOneMenu(int i, HMENU subMenu)\r
431 {\r
432     int j;\r
433     static MENUITEMINFO info;\r
434 \r
435     info.cbSize = sizeof(MENUITEMINFO);\r
436     info.fMask = MIIM_STATE | MIIM_TYPE;\r
437           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
438             char buf[MSG_SIZ];\r
439             info.dwTypeData = buf;\r
440             info.cch = sizeof(buf);\r
441             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
442             if(i < 10) {\r
443                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
444                 else menuText[i][j] = strdup(buf); // remember original on first change\r
445             }\r
446             if(buf[0] == NULLCHAR) continue;\r
447             info.dwTypeData = T_(buf);\r
448             info.cch = strlen(buf)+1;\r
449             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
450           }\r
451     return subMenu;\r
452 }\r
453 \r
454 void\r
455 TranslateMenus(int addLanguage)\r
456 {\r
457     int i;\r
458     WIN32_FIND_DATA fileData;\r
459     HANDLE hFind;\r
460 #define IDM_English 1970\r
461     if(1) {\r
462         HMENU mainMenu = GetMenu(hwndMain);\r
463         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
464           HMENU subMenu = GetSubMenu(mainMenu, i);\r
465           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
466                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
467           TranslateOneMenu(i, subMenu);\r
468         }\r
469         DrawMenuBar(hwndMain);\r
470     }\r
471 \r
472     if(!addLanguage) return;\r
473     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
474         HMENU mainMenu = GetMenu(hwndMain);\r
475         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
476         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
477         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
478         i = 0; lastChecked = IDM_English;\r
479         do {\r
480             char *p, *q = fileData.cFileName;\r
481             int checkFlag = MF_UNCHECKED;\r
482             languageFile[i] = strdup(q);\r
483             if(barbaric && !strcmp(oldLanguage, q)) {\r
484                 checkFlag = MF_CHECKED;\r
485                 lastChecked = IDM_English + i + 1;\r
486                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
487             }\r
488             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
489             p = strstr(fileData.cFileName, ".lng");\r
490             if(p) *p = 0;\r
491             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
492         } while(FindNextFile(hFind, &fileData));\r
493         FindClose(hFind);\r
494     }\r
495 }\r
496 \r
497 #endif\r
498 \r
499 #define IDM_RecentEngines 3000\r
500 \r
501 void\r
502 RecentEngineMenu (char *s)\r
503 {\r
504     if(appData.icsActive) return;\r
505     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
506         HMENU mainMenu = GetMenu(hwndMain);\r
507         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
508         int i=IDM_RecentEngines;\r
509         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
510         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
511         while(*s) {\r
512           char *p = strchr(s, '\n');\r
513           if(p == NULL) return; // malformed!\r
514           *p = NULLCHAR;\r
515           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
516           *p = '\n';\r
517           s = p+1;\r
518         }\r
519     }\r
520 }\r
521 \r
522 \r
523 typedef struct {\r
524   char *name;\r
525   int squareSize;\r
526   int lineGap;\r
527   int smallLayout;\r
528   int tinyLayout;\r
529   int cliWidth, cliHeight;\r
530 } SizeInfo;\r
531 \r
532 SizeInfo sizeInfo[] = \r
533 {\r
534   { "tiny",     21, 0, 1, 1, 0, 0 },\r
535   { "teeny",    25, 1, 1, 1, 0, 0 },\r
536   { "dinky",    29, 1, 1, 1, 0, 0 },\r
537   { "petite",   33, 1, 1, 1, 0, 0 },\r
538   { "slim",     37, 2, 1, 0, 0, 0 },\r
539   { "small",    40, 2, 1, 0, 0, 0 },\r
540   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
541   { "middling", 49, 2, 0, 0, 0, 0 },\r
542   { "average",  54, 2, 0, 0, 0, 0 },\r
543   { "moderate", 58, 3, 0, 0, 0, 0 },\r
544   { "medium",   64, 3, 0, 0, 0, 0 },\r
545   { "bulky",    72, 3, 0, 0, 0, 0 },\r
546   { "large",    80, 3, 0, 0, 0, 0 },\r
547   { "big",      87, 3, 0, 0, 0, 0 },\r
548   { "huge",     95, 3, 0, 0, 0, 0 },\r
549   { "giant",    108, 3, 0, 0, 0, 0 },\r
550   { "colossal", 116, 4, 0, 0, 0, 0 },\r
551   { "titanic",  129, 4, 0, 0, 0, 0 },\r
552   { NULL, 0, 0, 0, 0, 0, 0 }\r
553 };\r
554 \r
555 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
556 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
557 {\r
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576 };\r
577 \r
578 MyFont *font[NUM_SIZES][NUM_FONTS];\r
579 \r
580 typedef struct {\r
581   char *label;\r
582   int id;\r
583   HWND hwnd;\r
584   WNDPROC wndproc;\r
585 } MyButtonDesc;\r
586 \r
587 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
588 #define N_BUTTONS 5\r
589 \r
590 MyButtonDesc buttonDesc[N_BUTTONS] =\r
591 {\r
592   {"<<", IDM_ToStart, NULL, NULL},\r
593   {"<", IDM_Backward, NULL, NULL},\r
594   {"P", IDM_Pause, NULL, NULL},\r
595   {">", IDM_Forward, NULL, NULL},\r
596   {">>", IDM_ToEnd, NULL, NULL},\r
597 };\r
598 \r
599 int tinyLayout = 0, smallLayout = 0;\r
600 #define MENU_BAR_ITEMS 9\r
601 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
602   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
603   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
604 };\r
605 \r
606 \r
607 MySound sounds[(int)NSoundClasses];\r
608 MyTextAttribs textAttribs[(int)NColorClasses];\r
609 \r
610 MyColorizeAttribs colorizeAttribs[] = {\r
611   { (COLORREF)0, 0, N_("Shout Text") },\r
612   { (COLORREF)0, 0, N_("SShout/CShout") },\r
613   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
614   { (COLORREF)0, 0, N_("Channel Text") },\r
615   { (COLORREF)0, 0, N_("Kibitz Text") },\r
616   { (COLORREF)0, 0, N_("Tell Text") },\r
617   { (COLORREF)0, 0, N_("Challenge Text") },\r
618   { (COLORREF)0, 0, N_("Request Text") },\r
619   { (COLORREF)0, 0, N_("Seek Text") },\r
620   { (COLORREF)0, 0, N_("Normal Text") },\r
621   { (COLORREF)0, 0, N_("None") }\r
622 };\r
623 \r
624 \r
625 \r
626 static char *commentTitle;\r
627 static char *commentText;\r
628 static int commentIndex;\r
629 static Boolean editComment = FALSE;\r
630 \r
631 \r
632 char errorTitle[MSG_SIZ];\r
633 char errorMessage[2*MSG_SIZ];\r
634 HWND errorDialog = NULL;\r
635 BOOLEAN moveErrorMessageUp = FALSE;\r
636 BOOLEAN consoleEcho = TRUE;\r
637 CHARFORMAT consoleCF;\r
638 COLORREF consoleBackgroundColor;\r
639 \r
640 char *programVersion;\r
641 \r
642 #define CPReal 1\r
643 #define CPComm 2\r
644 #define CPSock 3\r
645 #define CPRcmd 4\r
646 typedef int CPKind;\r
647 \r
648 typedef struct {\r
649   CPKind kind;\r
650   HANDLE hProcess;\r
651   DWORD pid;\r
652   HANDLE hTo;\r
653   HANDLE hFrom;\r
654   SOCKET sock;\r
655   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
656 } ChildProc;\r
657 \r
658 #define INPUT_SOURCE_BUF_SIZE 4096\r
659 \r
660 typedef struct _InputSource {\r
661   CPKind kind;\r
662   HANDLE hFile;\r
663   SOCKET sock;\r
664   int lineByLine;\r
665   HANDLE hThread;\r
666   DWORD id;\r
667   char buf[INPUT_SOURCE_BUF_SIZE];\r
668   char *next;\r
669   DWORD count;\r
670   int error;\r
671   InputCallback func;\r
672   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
673   VOIDSTAR closure;\r
674 } InputSource;\r
675 \r
676 InputSource *consoleInputSource;\r
677 \r
678 DCB dcb;\r
679 \r
680 /* forward */\r
681 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
682 VOID ConsoleCreate();\r
683 LRESULT CALLBACK\r
684   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
685 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
686 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
687 VOID ParseCommSettings(char *arg, DCB *dcb);\r
688 LRESULT CALLBACK\r
689   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
690 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
691 void ParseIcsTextMenu(char *icsTextMenuString);\r
692 VOID PopUpNameDialog(char firstchar);\r
693 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
694 \r
695 /* [AS] */\r
696 int NewGameFRC();\r
697 int GameListOptions();\r
698 \r
699 int dummy; // [HGM] for obsolete args\r
700 \r
701 HWND hwndMain = NULL;        /* root window*/\r
702 HWND hwndConsole = NULL;\r
703 HWND commentDialog = NULL;\r
704 HWND moveHistoryDialog = NULL;\r
705 HWND evalGraphDialog = NULL;\r
706 HWND engineOutputDialog = NULL;\r
707 HWND gameListDialog = NULL;\r
708 HWND editTagsDialog = NULL;\r
709 \r
710 int commentUp = FALSE;\r
711 \r
712 WindowPlacement wpMain;\r
713 WindowPlacement wpConsole;\r
714 WindowPlacement wpComment;\r
715 WindowPlacement wpMoveHistory;\r
716 WindowPlacement wpEvalGraph;\r
717 WindowPlacement wpEngineOutput;\r
718 WindowPlacement wpGameList;\r
719 WindowPlacement wpTags;\r
720 \r
721 VOID EngineOptionsPopup(); // [HGM] settings\r
722 \r
723 VOID GothicPopUp(char *title, VariantClass variant);\r
724 /*\r
725  * Setting "frozen" should disable all user input other than deleting\r
726  * the window.  We do this while engines are initializing themselves.\r
727  */\r
728 static int frozen = 0;\r
729 static int oldMenuItemState[MENU_BAR_ITEMS];\r
730 void FreezeUI()\r
731 {\r
732   HMENU hmenu;\r
733   int i;\r
734 \r
735   if (frozen) return;\r
736   frozen = 1;\r
737   hmenu = GetMenu(hwndMain);\r
738   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
739     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
740   }\r
741   DrawMenuBar(hwndMain);\r
742 }\r
743 \r
744 /* Undo a FreezeUI */\r
745 void ThawUI()\r
746 {\r
747   HMENU hmenu;\r
748   int i;\r
749 \r
750   if (!frozen) return;\r
751   frozen = 0;\r
752   hmenu = GetMenu(hwndMain);\r
753   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
754     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
755   }\r
756   DrawMenuBar(hwndMain);\r
757 }\r
758 \r
759 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
760 \r
761 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
762 #ifdef JAWS\r
763 #include "jaws.c"\r
764 #else\r
765 #define JAWS_INIT\r
766 #define JAWS_ARGS\r
767 #define JAWS_ALT_INTERCEPT\r
768 #define JAWS_KBUP_NAVIGATION\r
769 #define JAWS_KBDOWN_NAVIGATION\r
770 #define JAWS_MENU_ITEMS\r
771 #define JAWS_SILENCE\r
772 #define JAWS_REPLAY\r
773 #define JAWS_ACCEL\r
774 #define JAWS_COPYRIGHT\r
775 #define JAWS_DELETE(X) X\r
776 #define SAYMACHINEMOVE()\r
777 #define SAY(X)\r
778 #endif\r
779 \r
780 /*---------------------------------------------------------------------------*\\r
781  *\r
782  * WinMain\r
783  *\r
784 \*---------------------------------------------------------------------------*/\r
785 \r
786 int APIENTRY\r
787 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
788         LPSTR lpCmdLine, int nCmdShow)\r
789 {\r
790   MSG msg;\r
791   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
792 //  INITCOMMONCONTROLSEX ex;\r
793 \r
794   debugFP = stderr;\r
795 \r
796   LoadLibrary("RICHED32.DLL");\r
797   consoleCF.cbSize = sizeof(CHARFORMAT);\r
798 \r
799   if (!InitApplication(hInstance)) {\r
800     return (FALSE);\r
801   }\r
802   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
803     return (FALSE);\r
804   }\r
805 \r
806   JAWS_INIT\r
807   TranslateMenus(1);\r
808 \r
809 //  InitCommonControlsEx(&ex);\r
810   InitCommonControls();\r
811 \r
812   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
813   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
814   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
815 \r
816   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
817 \r
818   while (GetMessage(&msg, /* message structure */\r
819                     NULL, /* handle of window receiving the message */\r
820                     0,    /* lowest message to examine */\r
821                     0))   /* highest message to examine */\r
822     {\r
823 \r
824       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
825         // [HGM] navigate: switch between all windows with tab\r
826         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
827         int i, currentElement = 0;\r
828 \r
829         // first determine what element of the chain we come from (if any)\r
830         if(appData.icsActive) {\r
831             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
832             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
833         }\r
834         if(engineOutputDialog && EngineOutputIsUp()) {\r
835             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
836             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
837         }\r
838         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
839             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
840         }\r
841         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
842         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
843         if(msg.hwnd == e1)                 currentElement = 2; else\r
844         if(msg.hwnd == e2)                 currentElement = 3; else\r
845         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
846         if(msg.hwnd == mh)                currentElement = 4; else\r
847         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
848         if(msg.hwnd == hText)  currentElement = 5; else\r
849         if(msg.hwnd == hInput) currentElement = 6; else\r
850         for (i = 0; i < N_BUTTONS; i++) {\r
851             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
852         }\r
853 \r
854         // determine where to go to\r
855         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
856           do {\r
857             currentElement = (currentElement + direction) % 7;\r
858             switch(currentElement) {\r
859                 case 0:\r
860                   h = hwndMain; break; // passing this case always makes the loop exit\r
861                 case 1:\r
862                   h = buttonDesc[0].hwnd; break; // could be NULL\r
863                 case 2:\r
864                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
865                   h = e1; break;\r
866                 case 3:\r
867                   if(!EngineOutputIsUp()) continue;\r
868                   h = e2; break;\r
869                 case 4:\r
870                   if(!MoveHistoryIsUp()) continue;\r
871                   h = mh; break;\r
872 //              case 6: // input to eval graph does not seem to get here!\r
873 //                if(!EvalGraphIsUp()) continue;\r
874 //                h = evalGraphDialog; break;\r
875                 case 5:\r
876                   if(!appData.icsActive) continue;\r
877                   SAY("display");\r
878                   h = hText; break;\r
879                 case 6:\r
880                   if(!appData.icsActive) continue;\r
881                   SAY("input");\r
882                   h = hInput; break;\r
883             }\r
884           } while(h == 0);\r
885 \r
886           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
887           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
888           SetFocus(h);\r
889 \r
890           continue; // this message now has been processed\r
891         }\r
892       }\r
893 \r
894       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
895           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
896           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
897           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
898           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
899           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
900           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
901           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
902           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
903           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
904         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
905         for(i=0; i<MAX_CHAT; i++) \r
906             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
907                 done = 1; break;\r
908         }\r
909         if(done) continue; // [HGM] chat: end patch\r
910         TranslateMessage(&msg); /* Translates virtual key codes */\r
911         DispatchMessage(&msg);  /* Dispatches message to window */\r
912       }\r
913     }\r
914 \r
915 \r
916   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
917 }\r
918 \r
919 /*---------------------------------------------------------------------------*\\r
920  *\r
921  * Initialization functions\r
922  *\r
923 \*---------------------------------------------------------------------------*/\r
924 \r
925 void\r
926 SetUserLogo()\r
927 {   // update user logo if necessary\r
928     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
929 \r
930     if(appData.autoLogo) {\r
931           curName = UserName();\r
932           if(strcmp(curName, oldUserName)) {\r
933                 GetCurrentDirectory(MSG_SIZ, dir);\r
934                 SetCurrentDirectory(installDir);\r
935                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
936                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
937                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
938                 if(userLogo == NULL)\r
939                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
940                 SetCurrentDirectory(dir); /* return to prev directory */\r
941           }\r
942     }\r
943 }\r
944 \r
945 BOOL\r
946 InitApplication(HINSTANCE hInstance)\r
947 {\r
948   WNDCLASS wc;\r
949 \r
950   /* Fill in window class structure with parameters that describe the */\r
951   /* main window. */\r
952 \r
953   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
954   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
955   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
956   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
957   wc.hInstance     = hInstance;         /* Owner of this class */\r
958   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
959   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
960   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
961   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
962   wc.lpszClassName = szAppName;                 /* Name to register as */\r
963 \r
964   /* Register the window class and return success/failure code. */\r
965   if (!RegisterClass(&wc)) return FALSE;\r
966 \r
967   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
968   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
969   wc.cbClsExtra    = 0;\r
970   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
971   wc.hInstance     = hInstance;\r
972   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
973   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
974   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
975   wc.lpszMenuName  = NULL;\r
976   wc.lpszClassName = szConsoleName;\r
977 \r
978   if (!RegisterClass(&wc)) return FALSE;\r
979   return TRUE;\r
980 }\r
981 \r
982 \r
983 /* Set by InitInstance, used by EnsureOnScreen */\r
984 int screenHeight, screenWidth;\r
985 \r
986 void\r
987 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
988 {\r
989 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
990   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
991   if (*x > screenWidth - 32) *x = 0;\r
992   if (*y > screenHeight - 32) *y = 0;\r
993   if (*x < minX) *x = minX;\r
994   if (*y < minY) *y = minY;\r
995 }\r
996 \r
997 VOID\r
998 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
999 {\r
1000   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1001   GetCurrentDirectory(MSG_SIZ, dir);\r
1002   SetCurrentDirectory(installDir);\r
1003   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1004       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1005 \r
1006       if (cps->programLogo == NULL && appData.debugMode) {\r
1007           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1008       }\r
1009   } else if(appData.autoLogo) {\r
1010       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1011         char *opponent = "";\r
1012         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1013         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1014         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1015         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1016             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1017             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1018         }\r
1019       } else\r
1020       if(appData.directory[n] && appData.directory[n][0]) {\r
1021         SetCurrentDirectory(appData.directory[n]);\r
1022         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1023       }\r
1024   }\r
1025   SetCurrentDirectory(dir); /* return to prev directory */\r
1026 }\r
1027 \r
1028 VOID\r
1029 InitTextures()\r
1030 {\r
1031   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1032   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1033   \r
1034   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1035       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1036       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1037       liteBackTextureMode = appData.liteBackTextureMode;\r
1038 \r
1039       if (liteBackTexture == NULL && appData.debugMode) {\r
1040           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1041       }\r
1042   }\r
1043   \r
1044   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1045       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1046       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1047       darkBackTextureMode = appData.darkBackTextureMode;\r
1048 \r
1049       if (darkBackTexture == NULL && appData.debugMode) {\r
1050           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1051       }\r
1052   }\r
1053 }\r
1054 \r
1055 BOOL\r
1056 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1057 {\r
1058   HWND hwnd; /* Main window handle. */\r
1059   int ibs;\r
1060   WINDOWPLACEMENT wp;\r
1061   char *filepart;\r
1062 \r
1063   hInst = hInstance;    /* Store instance handle in our global variable */\r
1064   programName = szAppName;\r
1065 \r
1066   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1067     *filepart = NULLCHAR;\r
1068     SetCurrentDirectory(installDir);\r
1069   } else {\r
1070     GetCurrentDirectory(MSG_SIZ, installDir);\r
1071   }\r
1072   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1073   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1074   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1075   /* xboard, and older WinBoards, controlled the move sound with the\r
1076      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1077      always turn the option on (so that the backend will call us),\r
1078      then let the user turn the sound off by setting it to silence if\r
1079      desired.  To accommodate old winboard.ini files saved by old\r
1080      versions of WinBoard, we also turn off the sound if the option\r
1081      was initially set to false. [HGM] taken out of InitAppData */\r
1082   if (!appData.ringBellAfterMoves) {\r
1083     sounds[(int)SoundMove].name = strdup("");\r
1084     appData.ringBellAfterMoves = TRUE;\r
1085   }\r
1086   if (appData.debugMode) {\r
1087     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1088     setbuf(debugFP, NULL);\r
1089   }\r
1090 \r
1091   LoadLanguageFile(appData.language);\r
1092 \r
1093   InitBackEnd1();\r
1094 \r
1095 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1096 //  InitEngineUCI( installDir, &second );\r
1097 \r
1098   /* Create a main window for this application instance. */\r
1099   hwnd = CreateWindow(szAppName, szTitle,\r
1100                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1101                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1102                       NULL, NULL, hInstance, NULL);\r
1103   hwndMain = hwnd;\r
1104 \r
1105   /* If window could not be created, return "failure" */\r
1106   if (!hwnd) {\r
1107     return (FALSE);\r
1108   }\r
1109 \r
1110   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1111   LoadLogo(&first, 0, FALSE);\r
1112   LoadLogo(&second, 1, appData.icsActive);\r
1113 \r
1114   SetUserLogo();\r
1115 \r
1116   iconWhite = LoadIcon(hInstance, "icon_white");\r
1117   iconBlack = LoadIcon(hInstance, "icon_black");\r
1118   iconCurrent = iconWhite;\r
1119   InitDrawingColors();\r
1120   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1121   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1122   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1123   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1124     /* Compute window size for each board size, and use the largest\r
1125        size that fits on this screen as the default. */\r
1126     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1127     if (boardSize == (BoardSize)-1 &&\r
1128         winH <= screenHeight\r
1129            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1130         && winW <= screenWidth) {\r
1131       boardSize = (BoardSize)ibs;\r
1132     }\r
1133   }\r
1134 \r
1135   InitDrawingSizes(boardSize, 0);\r
1136   RecentEngineMenu(appData.recentEngineList);\r
1137   InitMenuChecks();\r
1138   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1139 \r
1140   /* [AS] Load textures if specified */\r
1141   InitTextures();\r
1142 \r
1143   mysrandom( (unsigned) time(NULL) );\r
1144 \r
1145   /* [AS] Restore layout */\r
1146   if( wpMoveHistory.visible ) {\r
1147       MoveHistoryPopUp();\r
1148   }\r
1149 \r
1150   if( wpEvalGraph.visible ) {\r
1151       EvalGraphPopUp();\r
1152   }\r
1153 \r
1154   if( wpEngineOutput.visible ) {\r
1155       EngineOutputPopUp();\r
1156   }\r
1157 \r
1158   /* Make the window visible; update its client area; and return "success" */\r
1159   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1160   wp.length = sizeof(WINDOWPLACEMENT);\r
1161   wp.flags = 0;\r
1162   wp.showCmd = nCmdShow;\r
1163   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1164   wp.rcNormalPosition.left = wpMain.x;\r
1165   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1166   wp.rcNormalPosition.top = wpMain.y;\r
1167   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1168   SetWindowPlacement(hwndMain, &wp);\r
1169 \r
1170   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1171 \r
1172   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1173                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1174 \r
1175   if (hwndConsole) {\r
1176 #if AOT_CONSOLE\r
1177     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1178                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1179 #endif\r
1180     ShowWindow(hwndConsole, nCmdShow);\r
1181     SetActiveWindow(hwndConsole);\r
1182   }\r
1183   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1184   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1185 \r
1186   return TRUE;\r
1187 \r
1188 }\r
1189 \r
1190 VOID\r
1191 InitMenuChecks()\r
1192 {\r
1193   HMENU hmenu = GetMenu(hwndMain);\r
1194 \r
1195   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1196                         MF_BYCOMMAND|((appData.icsActive &&\r
1197                                        *appData.icsCommPort != NULLCHAR) ?\r
1198                                       MF_ENABLED : MF_GRAYED));\r
1199   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1200                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1201                                      MF_CHECKED : MF_UNCHECKED));\r
1202 }\r
1203 \r
1204 //---------------------------------------------------------------------------------------------------------\r
1205 \r
1206 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1207 #define XBOARD FALSE\r
1208 \r
1209 #define OPTCHAR "/"\r
1210 #define SEPCHAR "="\r
1211 #define TOPLEVEL 0\r
1212 \r
1213 #include "args.h"\r
1214 \r
1215 // front-end part of option handling\r
1216 \r
1217 VOID\r
1218 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1219 {\r
1220   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1221   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1222   DeleteDC(hdc);\r
1223   lf->lfWidth = 0;\r
1224   lf->lfEscapement = 0;\r
1225   lf->lfOrientation = 0;\r
1226   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1227   lf->lfItalic = mfp->italic;\r
1228   lf->lfUnderline = mfp->underline;\r
1229   lf->lfStrikeOut = mfp->strikeout;\r
1230   lf->lfCharSet = mfp->charset;\r
1231   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1232   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1233   lf->lfQuality = DEFAULT_QUALITY;\r
1234   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1235     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1236 }\r
1237 \r
1238 void\r
1239 CreateFontInMF(MyFont *mf)\r
1240\r
1241   LFfromMFP(&mf->lf, &mf->mfp);\r
1242   if (mf->hf) DeleteObject(mf->hf);\r
1243   mf->hf = CreateFontIndirect(&mf->lf);\r
1244 }\r
1245 \r
1246 // [HGM] This platform-dependent table provides the location for storing the color info\r
1247 void *\r
1248 colorVariable[] = {\r
1249   &whitePieceColor, \r
1250   &blackPieceColor, \r
1251   &lightSquareColor,\r
1252   &darkSquareColor, \r
1253   &highlightSquareColor,\r
1254   &premoveHighlightColor,\r
1255   NULL,\r
1256   &consoleBackgroundColor,\r
1257   &appData.fontForeColorWhite,\r
1258   &appData.fontBackColorWhite,\r
1259   &appData.fontForeColorBlack,\r
1260   &appData.fontBackColorBlack,\r
1261   &appData.evalHistColorWhite,\r
1262   &appData.evalHistColorBlack,\r
1263   &appData.highlightArrowColor,\r
1264 };\r
1265 \r
1266 /* Command line font name parser.  NULL name means do nothing.\r
1267    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1268    For backward compatibility, syntax without the colon is also\r
1269    accepted, but font names with digits in them won't work in that case.\r
1270 */\r
1271 VOID\r
1272 ParseFontName(char *name, MyFontParams *mfp)\r
1273 {\r
1274   char *p, *q;\r
1275   if (name == NULL) return;\r
1276   p = name;\r
1277   q = strchr(p, ':');\r
1278   if (q) {\r
1279     if (q - p >= sizeof(mfp->faceName))\r
1280       ExitArgError(_("Font name too long:"), name, TRUE);\r
1281     memcpy(mfp->faceName, p, q - p);\r
1282     mfp->faceName[q - p] = NULLCHAR;\r
1283     p = q + 1;\r
1284   } else {\r
1285     q = mfp->faceName;\r
1286 \r
1287     while (*p && !isdigit(*p)) {\r
1288       *q++ = *p++;\r
1289       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1290         ExitArgError(_("Font name too long:"), name, TRUE);\r
1291     }\r
1292     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1293     *q = NULLCHAR;\r
1294   }\r
1295   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1296   mfp->pointSize = (float) atof(p);\r
1297   mfp->bold = (strchr(p, 'b') != NULL);\r
1298   mfp->italic = (strchr(p, 'i') != NULL);\r
1299   mfp->underline = (strchr(p, 'u') != NULL);\r
1300   mfp->strikeout = (strchr(p, 's') != NULL);\r
1301   mfp->charset = DEFAULT_CHARSET;\r
1302   q = strchr(p, 'c');\r
1303   if (q)\r
1304     mfp->charset = (BYTE) atoi(q+1);\r
1305 }\r
1306 \r
1307 void\r
1308 ParseFont(char *name, int number)\r
1309 { // wrapper to shield back-end from 'font'\r
1310   ParseFontName(name, &font[boardSize][number]->mfp);\r
1311 }\r
1312 \r
1313 void\r
1314 SetFontDefaults()\r
1315 { // in WB  we have a 2D array of fonts; this initializes their description\r
1316   int i, j;\r
1317   /* Point font array elements to structures and\r
1318      parse default font names */\r
1319   for (i=0; i<NUM_FONTS; i++) {\r
1320     for (j=0; j<NUM_SIZES; j++) {\r
1321       font[j][i] = &fontRec[j][i];\r
1322       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1323     }\r
1324   }\r
1325 }\r
1326 \r
1327 void\r
1328 CreateFonts()\r
1329 { // here we create the actual fonts from the selected descriptions\r
1330   int i, j;\r
1331   for (i=0; i<NUM_FONTS; i++) {\r
1332     for (j=0; j<NUM_SIZES; j++) {\r
1333       CreateFontInMF(font[j][i]);\r
1334     }\r
1335   }\r
1336 }\r
1337 /* Color name parser.\r
1338    X version accepts X color names, but this one\r
1339    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1340 COLORREF\r
1341 ParseColorName(char *name)\r
1342 {\r
1343   int red, green, blue, count;\r
1344   char buf[MSG_SIZ];\r
1345 \r
1346   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1347   if (count != 3) {\r
1348     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1349       &red, &green, &blue);\r
1350   }\r
1351   if (count != 3) {\r
1352     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1353     DisplayError(buf, 0);\r
1354     return RGB(0, 0, 0);\r
1355   }\r
1356   return PALETTERGB(red, green, blue);\r
1357 }\r
1358 \r
1359 void\r
1360 ParseColor(int n, char *name)\r
1361 { // for WinBoard the color is an int, which needs to be derived from the string\r
1362   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1363 }\r
1364 \r
1365 void\r
1366 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1367 {\r
1368   char *e = argValue;\r
1369   int eff = 0;\r
1370 \r
1371   while (*e) {\r
1372     if (*e == 'b')      eff |= CFE_BOLD;\r
1373     else if (*e == 'i') eff |= CFE_ITALIC;\r
1374     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1375     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1376     else if (*e == '#' || isdigit(*e)) break;\r
1377     e++;\r
1378   }\r
1379   *effects = eff;\r
1380   *color   = ParseColorName(e);\r
1381 }\r
1382 \r
1383 void\r
1384 ParseTextAttribs(ColorClass cc, char *s)\r
1385 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1386     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1387     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1388 }\r
1389 \r
1390 void\r
1391 ParseBoardSize(void *addr, char *name)\r
1392 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1393   BoardSize bs = SizeTiny;\r
1394   while (sizeInfo[bs].name != NULL) {\r
1395     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1396         *(BoardSize *)addr = bs;\r
1397         return;\r
1398     }\r
1399     bs++;\r
1400   }\r
1401   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1402 }\r
1403 \r
1404 void\r
1405 LoadAllSounds()\r
1406 { // [HGM] import name from appData first\r
1407   ColorClass cc;\r
1408   SoundClass sc;\r
1409   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1410     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1411     textAttribs[cc].sound.data = NULL;\r
1412     MyLoadSound(&textAttribs[cc].sound);\r
1413   }\r
1414   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1415     textAttribs[cc].sound.name = strdup("");\r
1416     textAttribs[cc].sound.data = NULL;\r
1417   }\r
1418   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1419     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1420     sounds[sc].data = NULL;\r
1421     MyLoadSound(&sounds[sc]);\r
1422   }\r
1423 }\r
1424 \r
1425 void\r
1426 SetCommPortDefaults()\r
1427 {\r
1428    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1429   dcb.DCBlength = sizeof(DCB);\r
1430   dcb.BaudRate = 9600;\r
1431   dcb.fBinary = TRUE;\r
1432   dcb.fParity = FALSE;\r
1433   dcb.fOutxCtsFlow = FALSE;\r
1434   dcb.fOutxDsrFlow = FALSE;\r
1435   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1436   dcb.fDsrSensitivity = FALSE;\r
1437   dcb.fTXContinueOnXoff = TRUE;\r
1438   dcb.fOutX = FALSE;\r
1439   dcb.fInX = FALSE;\r
1440   dcb.fNull = FALSE;\r
1441   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1442   dcb.fAbortOnError = FALSE;\r
1443   dcb.ByteSize = 7;\r
1444   dcb.Parity = SPACEPARITY;\r
1445   dcb.StopBits = ONESTOPBIT;\r
1446 }\r
1447 \r
1448 // [HGM] args: these three cases taken out to stay in front-end\r
1449 void\r
1450 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1451 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1452         // while the curent board size determines the element. This system should be ported to XBoard.\r
1453         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1454         int bs;\r
1455         for (bs=0; bs<NUM_SIZES; bs++) {\r
1456           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1457           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1458           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1459             ad->argName, mfp->faceName, mfp->pointSize,\r
1460             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1461             mfp->bold ? "b" : "",\r
1462             mfp->italic ? "i" : "",\r
1463             mfp->underline ? "u" : "",\r
1464             mfp->strikeout ? "s" : "",\r
1465             (int)mfp->charset);\r
1466         }\r
1467       }\r
1468 \r
1469 void\r
1470 ExportSounds()\r
1471 { // [HGM] copy the names from the internal WB variables to appData\r
1472   ColorClass cc;\r
1473   SoundClass sc;\r
1474   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1475     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1476   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1477     (&appData.soundMove)[sc] = sounds[sc].name;\r
1478 }\r
1479 \r
1480 void\r
1481 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1482 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1483         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1484         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1485           (ta->effects & CFE_BOLD) ? "b" : "",\r
1486           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1487           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1488           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1489           (ta->effects) ? " " : "",\r
1490           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1491       }\r
1492 \r
1493 void\r
1494 SaveColor(FILE *f, ArgDescriptor *ad)\r
1495 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1496         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1497         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1498           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1499 }\r
1500 \r
1501 void\r
1502 SaveBoardSize(FILE *f, char *name, void *addr)\r
1503 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1504   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1505 }\r
1506 \r
1507 void\r
1508 ParseCommPortSettings(char *s)\r
1509 { // wrapper to keep dcb from back-end\r
1510   ParseCommSettings(s, &dcb);\r
1511 }\r
1512 \r
1513 void\r
1514 GetWindowCoords()\r
1515 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1516   GetActualPlacement(hwndMain, &wpMain);\r
1517   GetActualPlacement(hwndConsole, &wpConsole);\r
1518   GetActualPlacement(commentDialog, &wpComment);\r
1519   GetActualPlacement(editTagsDialog, &wpTags);\r
1520   GetActualPlacement(gameListDialog, &wpGameList);\r
1521   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1522   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1523   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1524 }\r
1525 \r
1526 void\r
1527 PrintCommPortSettings(FILE *f, char *name)\r
1528 { // wrapper to shield back-end from DCB\r
1529       PrintCommSettings(f, name, &dcb);\r
1530 }\r
1531 \r
1532 int\r
1533 MySearchPath(char *installDir, char *name, char *fullname)\r
1534 {\r
1535   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1536   if(name[0]== '%') {\r
1537     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1538     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1539       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1540       *strchr(buf, '%') = 0;\r
1541       strcat(fullname, getenv(buf));\r
1542       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1543     }\r
1544     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1545     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1546     return (int) strlen(fullname);\r
1547   }\r
1548   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1549 }\r
1550 \r
1551 int\r
1552 MyGetFullPathName(char *name, char *fullname)\r
1553 {\r
1554   char *dummy;\r
1555   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1556 }\r
1557 \r
1558 int\r
1559 MainWindowUp()\r
1560 { // [HGM] args: allows testing if main window is realized from back-end\r
1561   return hwndMain != NULL;\r
1562 }\r
1563 \r
1564 void\r
1565 PopUpStartupDialog()\r
1566 {\r
1567     FARPROC lpProc;\r
1568     \r
1569     LoadLanguageFile(appData.language);\r
1570     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1571     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1572     FreeProcInstance(lpProc);\r
1573 }\r
1574 \r
1575 /*---------------------------------------------------------------------------*\\r
1576  *\r
1577  * GDI board drawing routines\r
1578  *\r
1579 \*---------------------------------------------------------------------------*/\r
1580 \r
1581 /* [AS] Draw square using background texture */\r
1582 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1583 {\r
1584     XFORM   x;\r
1585 \r
1586     if( mode == 0 ) {\r
1587         return; /* Should never happen! */\r
1588     }\r
1589 \r
1590     SetGraphicsMode( dst, GM_ADVANCED );\r
1591 \r
1592     switch( mode ) {\r
1593     case 1:\r
1594         /* Identity */\r
1595         break;\r
1596     case 2:\r
1597         /* X reflection */\r
1598         x.eM11 = -1.0;\r
1599         x.eM12 = 0;\r
1600         x.eM21 = 0;\r
1601         x.eM22 = 1.0;\r
1602         x.eDx = (FLOAT) dw + dx - 1;\r
1603         x.eDy = 0;\r
1604         dx = 0;\r
1605         SetWorldTransform( dst, &x );\r
1606         break;\r
1607     case 3:\r
1608         /* Y reflection */\r
1609         x.eM11 = 1.0;\r
1610         x.eM12 = 0;\r
1611         x.eM21 = 0;\r
1612         x.eM22 = -1.0;\r
1613         x.eDx = 0;\r
1614         x.eDy = (FLOAT) dh + dy - 1;\r
1615         dy = 0;\r
1616         SetWorldTransform( dst, &x );\r
1617         break;\r
1618     case 4:\r
1619         /* X/Y flip */\r
1620         x.eM11 = 0;\r
1621         x.eM12 = 1.0;\r
1622         x.eM21 = 1.0;\r
1623         x.eM22 = 0;\r
1624         x.eDx = (FLOAT) dx;\r
1625         x.eDy = (FLOAT) dy;\r
1626         dx = 0;\r
1627         dy = 0;\r
1628         SetWorldTransform( dst, &x );\r
1629         break;\r
1630     }\r
1631 \r
1632     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1633 \r
1634     x.eM11 = 1.0;\r
1635     x.eM12 = 0;\r
1636     x.eM21 = 0;\r
1637     x.eM22 = 1.0;\r
1638     x.eDx = 0;\r
1639     x.eDy = 0;\r
1640     SetWorldTransform( dst, &x );\r
1641 \r
1642     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1643 }\r
1644 \r
1645 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1646 enum {\r
1647     PM_WP = (int) WhitePawn, \r
1648     PM_WN = (int) WhiteKnight, \r
1649     PM_WB = (int) WhiteBishop, \r
1650     PM_WR = (int) WhiteRook, \r
1651     PM_WQ = (int) WhiteQueen, \r
1652     PM_WF = (int) WhiteFerz, \r
1653     PM_WW = (int) WhiteWazir, \r
1654     PM_WE = (int) WhiteAlfil, \r
1655     PM_WM = (int) WhiteMan, \r
1656     PM_WO = (int) WhiteCannon, \r
1657     PM_WU = (int) WhiteUnicorn, \r
1658     PM_WH = (int) WhiteNightrider, \r
1659     PM_WA = (int) WhiteAngel, \r
1660     PM_WC = (int) WhiteMarshall, \r
1661     PM_WAB = (int) WhiteCardinal, \r
1662     PM_WD = (int) WhiteDragon, \r
1663     PM_WL = (int) WhiteLance, \r
1664     PM_WS = (int) WhiteCobra, \r
1665     PM_WV = (int) WhiteFalcon, \r
1666     PM_WSG = (int) WhiteSilver, \r
1667     PM_WG = (int) WhiteGrasshopper, \r
1668     PM_WK = (int) WhiteKing,\r
1669     PM_BP = (int) BlackPawn, \r
1670     PM_BN = (int) BlackKnight, \r
1671     PM_BB = (int) BlackBishop, \r
1672     PM_BR = (int) BlackRook, \r
1673     PM_BQ = (int) BlackQueen, \r
1674     PM_BF = (int) BlackFerz, \r
1675     PM_BW = (int) BlackWazir, \r
1676     PM_BE = (int) BlackAlfil, \r
1677     PM_BM = (int) BlackMan,\r
1678     PM_BO = (int) BlackCannon, \r
1679     PM_BU = (int) BlackUnicorn, \r
1680     PM_BH = (int) BlackNightrider, \r
1681     PM_BA = (int) BlackAngel, \r
1682     PM_BC = (int) BlackMarshall, \r
1683     PM_BG = (int) BlackGrasshopper, \r
1684     PM_BAB = (int) BlackCardinal,\r
1685     PM_BD = (int) BlackDragon,\r
1686     PM_BL = (int) BlackLance,\r
1687     PM_BS = (int) BlackCobra,\r
1688     PM_BV = (int) BlackFalcon,\r
1689     PM_BSG = (int) BlackSilver,\r
1690     PM_BK = (int) BlackKing\r
1691 };\r
1692 \r
1693 static HFONT hPieceFont = NULL;\r
1694 static HBITMAP hPieceMask[(int) EmptySquare];\r
1695 static HBITMAP hPieceFace[(int) EmptySquare];\r
1696 static int fontBitmapSquareSize = 0;\r
1697 static char pieceToFontChar[(int) EmptySquare] =\r
1698                               { 'p', 'n', 'b', 'r', 'q', \r
1699                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1700                       'k', 'o', 'm', 'v', 't', 'w', \r
1701                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1702                                                               'l' };\r
1703 \r
1704 extern BOOL SetCharTable( char *table, const char * map );\r
1705 /* [HGM] moved to backend.c */\r
1706 \r
1707 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1708 {\r
1709     HBRUSH hbrush;\r
1710     BYTE r1 = GetRValue( color );\r
1711     BYTE g1 = GetGValue( color );\r
1712     BYTE b1 = GetBValue( color );\r
1713     BYTE r2 = r1 / 2;\r
1714     BYTE g2 = g1 / 2;\r
1715     BYTE b2 = b1 / 2;\r
1716     RECT rc;\r
1717 \r
1718     /* Create a uniform background first */\r
1719     hbrush = CreateSolidBrush( color );\r
1720     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1721     FillRect( hdc, &rc, hbrush );\r
1722     DeleteObject( hbrush );\r
1723     \r
1724     if( mode == 1 ) {\r
1725         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1726         int steps = squareSize / 2;\r
1727         int i;\r
1728 \r
1729         for( i=0; i<steps; i++ ) {\r
1730             BYTE r = r1 - (r1-r2) * i / steps;\r
1731             BYTE g = g1 - (g1-g2) * i / steps;\r
1732             BYTE b = b1 - (b1-b2) * i / steps;\r
1733 \r
1734             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1735             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1736             FillRect( hdc, &rc, hbrush );\r
1737             DeleteObject(hbrush);\r
1738         }\r
1739     }\r
1740     else if( mode == 2 ) {\r
1741         /* Diagonal gradient, good more or less for every piece */\r
1742         POINT triangle[3];\r
1743         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1744         HBRUSH hbrush_old;\r
1745         int steps = squareSize;\r
1746         int i;\r
1747 \r
1748         triangle[0].x = squareSize - steps;\r
1749         triangle[0].y = squareSize;\r
1750         triangle[1].x = squareSize;\r
1751         triangle[1].y = squareSize;\r
1752         triangle[2].x = squareSize;\r
1753         triangle[2].y = squareSize - steps;\r
1754 \r
1755         for( i=0; i<steps; i++ ) {\r
1756             BYTE r = r1 - (r1-r2) * i / steps;\r
1757             BYTE g = g1 - (g1-g2) * i / steps;\r
1758             BYTE b = b1 - (b1-b2) * i / steps;\r
1759 \r
1760             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1761             hbrush_old = SelectObject( hdc, hbrush );\r
1762             Polygon( hdc, triangle, 3 );\r
1763             SelectObject( hdc, hbrush_old );\r
1764             DeleteObject(hbrush);\r
1765             triangle[0].x++;\r
1766             triangle[2].y++;\r
1767         }\r
1768 \r
1769         SelectObject( hdc, hpen );\r
1770     }\r
1771 }\r
1772 \r
1773 /*\r
1774     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1775     seems to work ok. The main problem here is to find the "inside" of a chess\r
1776     piece: follow the steps as explained below.\r
1777 */\r
1778 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1779 {\r
1780     HBITMAP hbm;\r
1781     HBITMAP hbm_old;\r
1782     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1783     RECT rc;\r
1784     SIZE sz;\r
1785     POINT pt;\r
1786     int backColor = whitePieceColor; \r
1787     int foreColor = blackPieceColor;\r
1788     \r
1789     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1790         backColor = appData.fontBackColorWhite;\r
1791         foreColor = appData.fontForeColorWhite;\r
1792     }\r
1793     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1794         backColor = appData.fontBackColorBlack;\r
1795         foreColor = appData.fontForeColorBlack;\r
1796     }\r
1797 \r
1798     /* Mask */\r
1799     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1800 \r
1801     hbm_old = SelectObject( hdc, hbm );\r
1802 \r
1803     rc.left = 0;\r
1804     rc.top = 0;\r
1805     rc.right = squareSize;\r
1806     rc.bottom = squareSize;\r
1807 \r
1808     /* Step 1: background is now black */\r
1809     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1810 \r
1811     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1812 \r
1813     pt.x = (squareSize - sz.cx) / 2;\r
1814     pt.y = (squareSize - sz.cy) / 2;\r
1815 \r
1816     SetBkMode( hdc, TRANSPARENT );\r
1817     SetTextColor( hdc, chroma );\r
1818     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1819     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1820 \r
1821     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1822     /* Step 3: the area outside the piece is filled with white */\r
1823 //    FloodFill( hdc, 0, 0, chroma );\r
1824     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1825     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1826     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1827     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1828     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1829     /* \r
1830         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1831         but if the start point is not inside the piece we're lost!\r
1832         There should be a better way to do this... if we could create a region or path\r
1833         from the fill operation we would be fine for example.\r
1834     */\r
1835 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1836     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1837 \r
1838     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1839         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1840         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1841 \r
1842         SelectObject( dc2, bm2 );\r
1843         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1844         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1845         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1846         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1847         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1848 \r
1849         DeleteDC( dc2 );\r
1850         DeleteObject( bm2 );\r
1851     }\r
1852 \r
1853     SetTextColor( hdc, 0 );\r
1854     /* \r
1855         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1856         draw the piece again in black for safety.\r
1857     */\r
1858     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1859 \r
1860     SelectObject( hdc, hbm_old );\r
1861 \r
1862     if( hPieceMask[index] != NULL ) {\r
1863         DeleteObject( hPieceMask[index] );\r
1864     }\r
1865 \r
1866     hPieceMask[index] = hbm;\r
1867 \r
1868     /* Face */\r
1869     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1870 \r
1871     SelectObject( hdc, hbm );\r
1872 \r
1873     {\r
1874         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1875         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1876         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1877 \r
1878         SelectObject( dc1, hPieceMask[index] );\r
1879         SelectObject( dc2, bm2 );\r
1880         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1881         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1882         \r
1883         /* \r
1884             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1885             the piece background and deletes (makes transparent) the rest.\r
1886             Thanks to that mask, we are free to paint the background with the greates\r
1887             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1888             We use this, to make gradients and give the pieces a "roundish" look.\r
1889         */\r
1890         SetPieceBackground( hdc, backColor, 2 );\r
1891         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1892 \r
1893         DeleteDC( dc2 );\r
1894         DeleteDC( dc1 );\r
1895         DeleteObject( bm2 );\r
1896     }\r
1897 \r
1898     SetTextColor( hdc, foreColor );\r
1899     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1900 \r
1901     SelectObject( hdc, hbm_old );\r
1902 \r
1903     if( hPieceFace[index] != NULL ) {\r
1904         DeleteObject( hPieceFace[index] );\r
1905     }\r
1906 \r
1907     hPieceFace[index] = hbm;\r
1908 }\r
1909 \r
1910 static int TranslatePieceToFontPiece( int piece )\r
1911 {\r
1912     switch( piece ) {\r
1913     case BlackPawn:\r
1914         return PM_BP;\r
1915     case BlackKnight:\r
1916         return PM_BN;\r
1917     case BlackBishop:\r
1918         return PM_BB;\r
1919     case BlackRook:\r
1920         return PM_BR;\r
1921     case BlackQueen:\r
1922         return PM_BQ;\r
1923     case BlackKing:\r
1924         return PM_BK;\r
1925     case WhitePawn:\r
1926         return PM_WP;\r
1927     case WhiteKnight:\r
1928         return PM_WN;\r
1929     case WhiteBishop:\r
1930         return PM_WB;\r
1931     case WhiteRook:\r
1932         return PM_WR;\r
1933     case WhiteQueen:\r
1934         return PM_WQ;\r
1935     case WhiteKing:\r
1936         return PM_WK;\r
1937 \r
1938     case BlackAngel:\r
1939         return PM_BA;\r
1940     case BlackMarshall:\r
1941         return PM_BC;\r
1942     case BlackFerz:\r
1943         return PM_BF;\r
1944     case BlackNightrider:\r
1945         return PM_BH;\r
1946     case BlackAlfil:\r
1947         return PM_BE;\r
1948     case BlackWazir:\r
1949         return PM_BW;\r
1950     case BlackUnicorn:\r
1951         return PM_BU;\r
1952     case BlackCannon:\r
1953         return PM_BO;\r
1954     case BlackGrasshopper:\r
1955         return PM_BG;\r
1956     case BlackMan:\r
1957         return PM_BM;\r
1958     case BlackSilver:\r
1959         return PM_BSG;\r
1960     case BlackLance:\r
1961         return PM_BL;\r
1962     case BlackFalcon:\r
1963         return PM_BV;\r
1964     case BlackCobra:\r
1965         return PM_BS;\r
1966     case BlackCardinal:\r
1967         return PM_BAB;\r
1968     case BlackDragon:\r
1969         return PM_BD;\r
1970 \r
1971     case WhiteAngel:\r
1972         return PM_WA;\r
1973     case WhiteMarshall:\r
1974         return PM_WC;\r
1975     case WhiteFerz:\r
1976         return PM_WF;\r
1977     case WhiteNightrider:\r
1978         return PM_WH;\r
1979     case WhiteAlfil:\r
1980         return PM_WE;\r
1981     case WhiteWazir:\r
1982         return PM_WW;\r
1983     case WhiteUnicorn:\r
1984         return PM_WU;\r
1985     case WhiteCannon:\r
1986         return PM_WO;\r
1987     case WhiteGrasshopper:\r
1988         return PM_WG;\r
1989     case WhiteMan:\r
1990         return PM_WM;\r
1991     case WhiteSilver:\r
1992         return PM_WSG;\r
1993     case WhiteLance:\r
1994         return PM_WL;\r
1995     case WhiteFalcon:\r
1996         return PM_WV;\r
1997     case WhiteCobra:\r
1998         return PM_WS;\r
1999     case WhiteCardinal:\r
2000         return PM_WAB;\r
2001     case WhiteDragon:\r
2002         return PM_WD;\r
2003     }\r
2004 \r
2005     return 0;\r
2006 }\r
2007 \r
2008 void CreatePiecesFromFont()\r
2009 {\r
2010     LOGFONT lf;\r
2011     HDC hdc_window = NULL;\r
2012     HDC hdc = NULL;\r
2013     HFONT hfont_old;\r
2014     int fontHeight;\r
2015     int i;\r
2016 \r
2017     if( fontBitmapSquareSize < 0 ) {\r
2018         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2019         return;\r
2020     }\r
2021 \r
2022     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2023             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2024         fontBitmapSquareSize = -1;\r
2025         return;\r
2026     }\r
2027 \r
2028     if( fontBitmapSquareSize != squareSize ) {\r
2029         hdc_window = GetDC( hwndMain );\r
2030         hdc = CreateCompatibleDC( hdc_window );\r
2031 \r
2032         if( hPieceFont != NULL ) {\r
2033             DeleteObject( hPieceFont );\r
2034         }\r
2035         else {\r
2036             for( i=0; i<=(int)BlackKing; i++ ) {\r
2037                 hPieceMask[i] = NULL;\r
2038                 hPieceFace[i] = NULL;\r
2039             }\r
2040         }\r
2041 \r
2042         fontHeight = 75;\r
2043 \r
2044         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2045             fontHeight = appData.fontPieceSize;\r
2046         }\r
2047 \r
2048         fontHeight = (fontHeight * squareSize) / 100;\r
2049 \r
2050         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2051         lf.lfWidth = 0;\r
2052         lf.lfEscapement = 0;\r
2053         lf.lfOrientation = 0;\r
2054         lf.lfWeight = FW_NORMAL;\r
2055         lf.lfItalic = 0;\r
2056         lf.lfUnderline = 0;\r
2057         lf.lfStrikeOut = 0;\r
2058         lf.lfCharSet = DEFAULT_CHARSET;\r
2059         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2060         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2061         lf.lfQuality = PROOF_QUALITY;\r
2062         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2063         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2064         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2065 \r
2066         hPieceFont = CreateFontIndirect( &lf );\r
2067 \r
2068         if( hPieceFont == NULL ) {\r
2069             fontBitmapSquareSize = -2;\r
2070         }\r
2071         else {\r
2072             /* Setup font-to-piece character table */\r
2073             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2074                 /* No (or wrong) global settings, try to detect the font */\r
2075                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2076                     /* Alpha */\r
2077                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2078                 }\r
2079                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2080                     /* DiagramTT* family */\r
2081                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2082                 }\r
2083                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2084                     /* Fairy symbols */\r
2085                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2086                 }\r
2087                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2088                     /* Good Companion (Some characters get warped as literal :-( */\r
2089                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2090                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2091                     SetCharTable(pieceToFontChar, s);\r
2092                 }\r
2093                 else {\r
2094                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2095                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2096                 }\r
2097             }\r
2098 \r
2099             /* Create bitmaps */\r
2100             hfont_old = SelectObject( hdc, hPieceFont );\r
2101             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2102                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2103                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2104 \r
2105             SelectObject( hdc, hfont_old );\r
2106 \r
2107             fontBitmapSquareSize = squareSize;\r
2108         }\r
2109     }\r
2110 \r
2111     if( hdc != NULL ) {\r
2112         DeleteDC( hdc );\r
2113     }\r
2114 \r
2115     if( hdc_window != NULL ) {\r
2116         ReleaseDC( hwndMain, hdc_window );\r
2117     }\r
2118 }\r
2119 \r
2120 HBITMAP\r
2121 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2122 {\r
2123   char name[128], buf[MSG_SIZ];\r
2124 \r
2125     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2126   if(appData.pieceDirectory[0]) {\r
2127     HBITMAP res;\r
2128     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2129     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2130     if(res) return res;\r
2131   }\r
2132   if (gameInfo.event &&\r
2133       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2134       strcmp(name, "k80s") == 0) {\r
2135     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2136   }\r
2137   return LoadBitmap(hinst, name);\r
2138 }\r
2139 \r
2140 \r
2141 /* Insert a color into the program's logical palette\r
2142    structure.  This code assumes the given color is\r
2143    the result of the RGB or PALETTERGB macro, and it\r
2144    knows how those macros work (which is documented).\r
2145 */\r
2146 VOID\r
2147 InsertInPalette(COLORREF color)\r
2148 {\r
2149   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2150 \r
2151   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2152     DisplayFatalError(_("Too many colors"), 0, 1);\r
2153     pLogPal->palNumEntries--;\r
2154     return;\r
2155   }\r
2156 \r
2157   pe->peFlags = (char) 0;\r
2158   pe->peRed = (char) (0xFF & color);\r
2159   pe->peGreen = (char) (0xFF & (color >> 8));\r
2160   pe->peBlue = (char) (0xFF & (color >> 16));\r
2161   return;\r
2162 }\r
2163 \r
2164 \r
2165 VOID\r
2166 InitDrawingColors()\r
2167 {\r
2168   int i;\r
2169   if (pLogPal == NULL) {\r
2170     /* Allocate enough memory for a logical palette with\r
2171      * PALETTESIZE entries and set the size and version fields\r
2172      * of the logical palette structure.\r
2173      */\r
2174     pLogPal = (NPLOGPALETTE)\r
2175       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2176                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2177     pLogPal->palVersion    = 0x300;\r
2178   }\r
2179   pLogPal->palNumEntries = 0;\r
2180 \r
2181   InsertInPalette(lightSquareColor);\r
2182   InsertInPalette(darkSquareColor);\r
2183   InsertInPalette(whitePieceColor);\r
2184   InsertInPalette(blackPieceColor);\r
2185   InsertInPalette(highlightSquareColor);\r
2186   InsertInPalette(premoveHighlightColor);\r
2187 \r
2188   /*  create a logical color palette according the information\r
2189    *  in the LOGPALETTE structure.\r
2190    */\r
2191   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2192 \r
2193   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2194   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2195   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2196   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2197   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2198   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2199   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2200     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2201 \r
2202    /* [AS] Force rendering of the font-based pieces */\r
2203   if( fontBitmapSquareSize > 0 ) {\r
2204     fontBitmapSquareSize = 0;\r
2205   }\r
2206 }\r
2207 \r
2208 \r
2209 int\r
2210 BoardWidth(int boardSize, int n)\r
2211 { /* [HGM] argument n added to allow different width and height */\r
2212   int lineGap = sizeInfo[boardSize].lineGap;\r
2213 \r
2214   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2215       lineGap = appData.overrideLineGap;\r
2216   }\r
2217 \r
2218   return (n + 1) * lineGap +\r
2219           n * sizeInfo[boardSize].squareSize;\r
2220 }\r
2221 \r
2222 /* Respond to board resize by dragging edge */\r
2223 VOID\r
2224 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2225 {\r
2226   BoardSize newSize = NUM_SIZES - 1;\r
2227   static int recurse = 0;\r
2228   if (IsIconic(hwndMain)) return;\r
2229   if (recurse > 0) return;\r
2230   recurse++;\r
2231   while (newSize > 0) {\r
2232         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2233         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2234            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2235     newSize--;\r
2236   } \r
2237   boardSize = newSize;\r
2238   InitDrawingSizes(boardSize, flags);\r
2239   recurse--;\r
2240 }\r
2241 \r
2242 \r
2243 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2244 \r
2245 VOID\r
2246 InitDrawingSizes(BoardSize boardSize, int flags)\r
2247 {\r
2248   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2249   ChessSquare piece;\r
2250   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2251   HDC hdc;\r
2252   SIZE clockSize, messageSize;\r
2253   HFONT oldFont;\r
2254   char buf[MSG_SIZ];\r
2255   char *str;\r
2256   HMENU hmenu = GetMenu(hwndMain);\r
2257   RECT crect, wrect, oldRect;\r
2258   int offby;\r
2259   LOGBRUSH logbrush;\r
2260   VariantClass v = gameInfo.variant;\r
2261 \r
2262   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2263   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2264 \r
2265   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2266   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2267   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2268   oldBoardSize = boardSize;\r
2269 \r
2270   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2271   { // correct board size to one where built-in pieces exist\r
2272     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2273        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2274       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2275       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2276       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2277       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2278       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2279                                    boardSize = SizeMiddling;\r
2280     }\r
2281   }\r
2282   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2283 \r
2284   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2285   oldRect.top = wpMain.y;\r
2286   oldRect.right = wpMain.x + wpMain.width;\r
2287   oldRect.bottom = wpMain.y + wpMain.height;\r
2288 \r
2289   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2290   smallLayout = sizeInfo[boardSize].smallLayout;\r
2291   squareSize = sizeInfo[boardSize].squareSize;\r
2292   lineGap = sizeInfo[boardSize].lineGap;\r
2293   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2294   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2295 \r
2296   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2297       lineGap = appData.overrideLineGap;\r
2298   }\r
2299 \r
2300   if (tinyLayout != oldTinyLayout) {\r
2301     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2302     if (tinyLayout) {\r
2303       style &= ~WS_SYSMENU;\r
2304       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2305                  "&Minimize\tCtrl+F4");\r
2306     } else {\r
2307       style |= WS_SYSMENU;\r
2308       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2309     }\r
2310     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2311 \r
2312     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2313       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2314         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2315     }\r
2316     DrawMenuBar(hwndMain);\r
2317   }\r
2318 \r
2319   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2320   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2321 \r
2322   /* Get text area sizes */\r
2323   hdc = GetDC(hwndMain);\r
2324   if (appData.clockMode) {\r
2325     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2326   } else {\r
2327     snprintf(buf, MSG_SIZ, _("White"));\r
2328   }\r
2329   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2330   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2331   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2332   str = _("We only care about the height here");\r
2333   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2334   SelectObject(hdc, oldFont);\r
2335   ReleaseDC(hwndMain, hdc);\r
2336 \r
2337   /* Compute where everything goes */\r
2338   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2339         /* [HGM] logo: if either logo is on, reserve space for it */\r
2340         logoHeight =  2*clockSize.cy;\r
2341         leftLogoRect.left   = OUTER_MARGIN;\r
2342         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2343         leftLogoRect.top    = OUTER_MARGIN;\r
2344         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2345 \r
2346         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2347         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2348         rightLogoRect.top    = OUTER_MARGIN;\r
2349         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2350 \r
2351 \r
2352     whiteRect.left = leftLogoRect.right;\r
2353     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2354     whiteRect.top = OUTER_MARGIN;\r
2355     whiteRect.bottom = whiteRect.top + logoHeight;\r
2356 \r
2357     blackRect.right = rightLogoRect.left;\r
2358     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2359     blackRect.top = whiteRect.top;\r
2360     blackRect.bottom = whiteRect.bottom;\r
2361   } else {\r
2362     whiteRect.left = OUTER_MARGIN;\r
2363     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2364     whiteRect.top = OUTER_MARGIN;\r
2365     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2366 \r
2367     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2368     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2369     blackRect.top = whiteRect.top;\r
2370     blackRect.bottom = whiteRect.bottom;\r
2371 \r
2372     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2373   }\r
2374 \r
2375   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2376   if (appData.showButtonBar) {\r
2377     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2378       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2379   } else {\r
2380     messageRect.right = OUTER_MARGIN + boardWidth;\r
2381   }\r
2382   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2383   messageRect.bottom = messageRect.top + messageSize.cy;\r
2384 \r
2385   boardRect.left = OUTER_MARGIN;\r
2386   boardRect.right = boardRect.left + boardWidth;\r
2387   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2388   boardRect.bottom = boardRect.top + boardHeight;\r
2389 \r
2390   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2391   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2392   oldTinyLayout = tinyLayout;\r
2393   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2394   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2395     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2396   winW *= 1 + twoBoards;\r
2397   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2398   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2399   wpMain.height = winH; //       without disturbing window attachments\r
2400   GetWindowRect(hwndMain, &wrect);\r
2401   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2402                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2403 \r
2404   // [HGM] placement: let attached windows follow size change.\r
2405   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2406   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2407   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2408   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2409   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2410 \r
2411   /* compensate if menu bar wrapped */\r
2412   GetClientRect(hwndMain, &crect);\r
2413   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2414   wpMain.height += offby;\r
2415   switch (flags) {\r
2416   case WMSZ_TOPLEFT:\r
2417     SetWindowPos(hwndMain, NULL, \r
2418                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2419                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2420     break;\r
2421 \r
2422   case WMSZ_TOPRIGHT:\r
2423   case WMSZ_TOP:\r
2424     SetWindowPos(hwndMain, NULL, \r
2425                  wrect.left, wrect.bottom - wpMain.height, \r
2426                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2427     break;\r
2428 \r
2429   case WMSZ_BOTTOMLEFT:\r
2430   case WMSZ_LEFT:\r
2431     SetWindowPos(hwndMain, NULL, \r
2432                  wrect.right - wpMain.width, wrect.top, \r
2433                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2434     break;\r
2435 \r
2436   case WMSZ_BOTTOMRIGHT:\r
2437   case WMSZ_BOTTOM:\r
2438   case WMSZ_RIGHT:\r
2439   default:\r
2440     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2441                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2442     break;\r
2443   }\r
2444 \r
2445   hwndPause = NULL;\r
2446   for (i = 0; i < N_BUTTONS; i++) {\r
2447     if (buttonDesc[i].hwnd != NULL) {\r
2448       DestroyWindow(buttonDesc[i].hwnd);\r
2449       buttonDesc[i].hwnd = NULL;\r
2450     }\r
2451     if (appData.showButtonBar) {\r
2452       buttonDesc[i].hwnd =\r
2453         CreateWindow("BUTTON", buttonDesc[i].label,\r
2454                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2455                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2456                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2457                      (HMENU) buttonDesc[i].id,\r
2458                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2459       if (tinyLayout) {\r
2460         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2461                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2462                     MAKELPARAM(FALSE, 0));\r
2463       }\r
2464       if (buttonDesc[i].id == IDM_Pause)\r
2465         hwndPause = buttonDesc[i].hwnd;\r
2466       buttonDesc[i].wndproc = (WNDPROC)\r
2467         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2468     }\r
2469   }\r
2470   if (gridPen != NULL) DeleteObject(gridPen);\r
2471   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2472   if (premovePen != NULL) DeleteObject(premovePen);\r
2473   if (lineGap != 0) {\r
2474     logbrush.lbStyle = BS_SOLID;\r
2475     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2476     gridPen =\r
2477       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2478                    lineGap, &logbrush, 0, NULL);\r
2479     logbrush.lbColor = highlightSquareColor;\r
2480     highlightPen =\r
2481       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2482                    lineGap, &logbrush, 0, NULL);\r
2483 \r
2484     logbrush.lbColor = premoveHighlightColor; \r
2485     premovePen =\r
2486       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2487                    lineGap, &logbrush, 0, NULL);\r
2488 \r
2489     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2490     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2491       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2492       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2493         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2494       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2495         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2496       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2497     }\r
2498     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2499       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2500       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2501         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2502         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2503       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2504         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2505       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2506     }\r
2507   }\r
2508 \r
2509   /* [HGM] Licensing requirement */\r
2510 #ifdef GOTHIC\r
2511   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2512 #endif\r
2513 #ifdef FALCON\r
2514   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2515 #endif\r
2516   GothicPopUp( "", VariantNormal);\r
2517 \r
2518 \r
2519 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2520 \r
2521   /* Load piece bitmaps for this board size */\r
2522   for (i=0; i<=2; i++) {\r
2523     for (piece = WhitePawn;\r
2524          (int) piece < (int) BlackPawn;\r
2525          piece = (ChessSquare) ((int) piece + 1)) {\r
2526       if (pieceBitmap[i][piece] != NULL)\r
2527         DeleteObject(pieceBitmap[i][piece]);\r
2528     }\r
2529   }\r
2530 \r
2531   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2532   // Orthodox Chess pieces\r
2533   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2534   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2535   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2536   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2537   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2538   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2539   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2540   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2541   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2542   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2543   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2544   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2545   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2546   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2547   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2548   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2549     // in Shogi, Hijack the unused Queen for Lance\r
2550     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2551     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2552     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2553   } else {\r
2554     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2555     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2556     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2557   }\r
2558 \r
2559   if(squareSize <= 72 && squareSize >= 33) { \r
2560     /* A & C are available in most sizes now */\r
2561     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2562       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2563       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2564       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2565       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2566       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2567       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2568       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2569       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2570       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2571       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2572       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2573       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2574     } else { // Smirf-like\r
2575       if(gameInfo.variant == VariantSChess) {\r
2576         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2577         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2578         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2579       } else {\r
2580         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2581         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2582         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2583       }\r
2584     }\r
2585     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2586       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2587       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2588       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2589     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2590       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2591       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2592       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2593     } else { // WinBoard standard\r
2594       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2595       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2596       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2597     }\r
2598   }\r
2599 \r
2600 \r
2601   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2602     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2603     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2604     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2605     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2606     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2607     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2608     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2609     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2610     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2611     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2612     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2613     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2614     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2615     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2616     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2617     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2618     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2619     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2620     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2621     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2622     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2623     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2624     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2625     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2626     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2627     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2628     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2629     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2630     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2631     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2632 \r
2633     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2634       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2635       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2636       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2637       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2638       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2639       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2640       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2641       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2642       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2643       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2644       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2645       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2646     } else {\r
2647       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2648       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2649       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2650       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2651       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2652       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2653       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2654       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2655       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2656       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2657       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2658       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2659     }\r
2660 \r
2661   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2662     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2663     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2664     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2665     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2666     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2667     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2668     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2669     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2670     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2671     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2672     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2673     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2674     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2675     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2676   }\r
2677 \r
2678 \r
2679   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2680   /* special Shogi support in this size */\r
2681   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2682       for (piece = WhitePawn;\r
2683            (int) piece < (int) BlackPawn;\r
2684            piece = (ChessSquare) ((int) piece + 1)) {\r
2685         if (pieceBitmap[i][piece] != NULL)\r
2686           DeleteObject(pieceBitmap[i][piece]);\r
2687       }\r
2688     }\r
2689   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2690   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2691   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2692   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2693   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2694   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2695   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2696   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2697   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2698   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2699   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2700   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2701   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2702   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2703   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2704   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2705   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2706   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2707   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2708   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2709   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2710   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2711   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2712   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2713   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2714   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2715   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2716   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2717   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2718   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2719   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2720   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2721   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2722   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2723   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2724   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2725   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2726   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2727   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2728   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2729   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2730   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2731   minorSize = 0;\r
2732   }\r
2733 }\r
2734 \r
2735 HBITMAP\r
2736 PieceBitmap(ChessSquare p, int kind)\r
2737 {\r
2738   if ((int) p >= (int) BlackPawn)\r
2739     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2740 \r
2741   return pieceBitmap[kind][(int) p];\r
2742 }\r
2743 \r
2744 /***************************************************************/\r
2745 \r
2746 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2747 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2748 /*\r
2749 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2750 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2751 */\r
2752 \r
2753 VOID\r
2754 SquareToPos(int row, int column, int * x, int * y)\r
2755 {\r
2756   if (flipView) {\r
2757     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2758     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2759   } else {\r
2760     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2761     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2762   }\r
2763 }\r
2764 \r
2765 VOID\r
2766 DrawCoordsOnDC(HDC hdc)\r
2767 {\r
2768   static char files[] = "0123456789012345678901221098765432109876543210";\r
2769   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2770   char str[2] = { NULLCHAR, NULLCHAR };\r
2771   int oldMode, oldAlign, x, y, start, i;\r
2772   HFONT oldFont;\r
2773   HBRUSH oldBrush;\r
2774 \r
2775   if (!appData.showCoords)\r
2776     return;\r
2777 \r
2778   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2779 \r
2780   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2781   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2782   oldAlign = GetTextAlign(hdc);\r
2783   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2784 \r
2785   y = boardRect.top + lineGap;\r
2786   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2787 \r
2788   if(border) {\r
2789     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2790     x += border - lineGap - 4; y += squareSize - 6;\r
2791   } else\r
2792   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2793   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2794     str[0] = files[start + i];\r
2795     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2796     y += squareSize + lineGap;\r
2797   }\r
2798 \r
2799   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2800 \r
2801   if(border) {\r
2802     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2803     x += -border + 4; y += border - squareSize + 6;\r
2804   } else\r
2805   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2806   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2807     str[0] = ranks[start + i];\r
2808     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2809     x += squareSize + lineGap;\r
2810   }    \r
2811 \r
2812   SelectObject(hdc, oldBrush);\r
2813   SetBkMode(hdc, oldMode);\r
2814   SetTextAlign(hdc, oldAlign);\r
2815   SelectObject(hdc, oldFont);\r
2816 }\r
2817 \r
2818 VOID\r
2819 DrawGridOnDC(HDC hdc)\r
2820 {\r
2821   HPEN oldPen;\r
2822  \r
2823   if (lineGap != 0) {\r
2824     oldPen = SelectObject(hdc, gridPen);\r
2825     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2826     SelectObject(hdc, oldPen);\r
2827   }\r
2828 }\r
2829 \r
2830 #define HIGHLIGHT_PEN 0\r
2831 #define PREMOVE_PEN   1\r
2832 \r
2833 VOID\r
2834 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2835 {\r
2836   int x1, y1;\r
2837   HPEN oldPen, hPen;\r
2838   if (lineGap == 0) return;\r
2839   if (flipView) {\r
2840     x1 = boardRect.left +\r
2841       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2842     y1 = boardRect.top +\r
2843       lineGap/2 + y * (squareSize + lineGap) + border;\r
2844   } else {\r
2845     x1 = boardRect.left +\r
2846       lineGap/2 + x * (squareSize + lineGap) + border;\r
2847     y1 = boardRect.top +\r
2848       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2849   }\r
2850   hPen = pen ? premovePen : highlightPen;\r
2851   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2852   MoveToEx(hdc, x1, y1, NULL);\r
2853   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2854   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2855   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2856   LineTo(hdc, x1, y1);\r
2857   SelectObject(hdc, oldPen);\r
2858 }\r
2859 \r
2860 VOID\r
2861 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2862 {\r
2863   int i;\r
2864   for (i=0; i<2; i++) {\r
2865     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2866       DrawHighlightOnDC(hdc, TRUE,\r
2867                         h->sq[i].x, h->sq[i].y,\r
2868                         pen);\r
2869   }\r
2870 }\r
2871 \r
2872 /* Note: sqcolor is used only in monoMode */\r
2873 /* Note that this code is largely duplicated in woptions.c,\r
2874    function DrawSampleSquare, so that needs to be updated too */\r
2875 VOID\r
2876 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2877 {\r
2878   HBITMAP oldBitmap;\r
2879   HBRUSH oldBrush;\r
2880   int tmpSize;\r
2881 \r
2882   if (appData.blindfold) return;\r
2883 \r
2884   /* [AS] Use font-based pieces if needed */\r
2885   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2886     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2887     CreatePiecesFromFont();\r
2888 \r
2889     if( fontBitmapSquareSize == squareSize ) {\r
2890         int index = TranslatePieceToFontPiece(piece);\r
2891 \r
2892         SelectObject( tmphdc, hPieceMask[ index ] );\r
2893 \r
2894       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2895         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2896       else\r
2897         BitBlt( hdc,\r
2898             x, y,\r
2899             squareSize, squareSize,\r
2900             tmphdc,\r
2901             0, 0,\r
2902             SRCAND );\r
2903 \r
2904         SelectObject( tmphdc, hPieceFace[ index ] );\r
2905 \r
2906       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2907         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2908       else\r
2909         BitBlt( hdc,\r
2910             x, y,\r
2911             squareSize, squareSize,\r
2912             tmphdc,\r
2913             0, 0,\r
2914             SRCPAINT );\r
2915 \r
2916         return;\r
2917     }\r
2918   }\r
2919 \r
2920   if (appData.monoMode) {\r
2921     SelectObject(tmphdc, PieceBitmap(piece, \r
2922       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2923     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2924            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2925   } else {\r
2926     HBRUSH xBrush = whitePieceBrush;\r
2927     tmpSize = squareSize;\r
2928     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2929     if(minorSize &&\r
2930         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2931          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2932       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2933       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2934       x += (squareSize - minorSize)>>1;\r
2935       y += squareSize - minorSize - 2;\r
2936       tmpSize = minorSize;\r
2937     }\r
2938     if (color || appData.allWhite ) {\r
2939       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2940       if( color )\r
2941               oldBrush = SelectObject(hdc, xBrush);\r
2942       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2943       if(appData.upsideDown && color==flipView)\r
2944         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2945       else\r
2946         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2947       /* Use black for outline of white pieces */\r
2948       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2949       if(appData.upsideDown && color==flipView)\r
2950         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2951       else\r
2952         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2953     } else if(appData.pieceDirectory[0]) {\r
2954       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2955       oldBrush = SelectObject(hdc, xBrush);\r
2956       if(appData.upsideDown && color==flipView)\r
2957         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2958       else\r
2959         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2960       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2961       if(appData.upsideDown && color==flipView)\r
2962         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2963       else\r
2964         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2965     } else {\r
2966       /* Use square color for details of black pieces */\r
2967       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2968       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2969       if(appData.upsideDown && !flipView)\r
2970         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2971       else\r
2972         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2973     }\r
2974     SelectObject(hdc, oldBrush);\r
2975     SelectObject(tmphdc, oldBitmap);\r
2976   }\r
2977 }\r
2978 \r
2979 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2980 int GetBackTextureMode( int algo )\r
2981 {\r
2982     int result = BACK_TEXTURE_MODE_DISABLED;\r
2983 \r
2984     switch( algo ) \r
2985     {\r
2986         case BACK_TEXTURE_MODE_PLAIN:\r
2987             result = 1; /* Always use identity map */\r
2988             break;\r
2989         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2990             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2991             break;\r
2992     }\r
2993 \r
2994     return result;\r
2995 }\r
2996 \r
2997 /* \r
2998     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2999     to handle redraws cleanly (as random numbers would always be different).\r
3000 */\r
3001 VOID RebuildTextureSquareInfo()\r
3002 {\r
3003     BITMAP bi;\r
3004     int lite_w = 0;\r
3005     int lite_h = 0;\r
3006     int dark_w = 0;\r
3007     int dark_h = 0;\r
3008     int row;\r
3009     int col;\r
3010 \r
3011     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3012 \r
3013     if( liteBackTexture != NULL ) {\r
3014         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3015             lite_w = bi.bmWidth;\r
3016             lite_h = bi.bmHeight;\r
3017         }\r
3018     }\r
3019 \r
3020     if( darkBackTexture != NULL ) {\r
3021         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3022             dark_w = bi.bmWidth;\r
3023             dark_h = bi.bmHeight;\r
3024         }\r
3025     }\r
3026 \r
3027     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3028         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3029             if( (col + row) & 1 ) {\r
3030                 /* Lite square */\r
3031                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3032                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3033                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3034                   else\r
3035                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3036                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3037                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3038                   else\r
3039                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3040                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3041                 }\r
3042             }\r
3043             else {\r
3044                 /* Dark square */\r
3045                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3046                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3047                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3048                   else\r
3049                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3050                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3051                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3052                   else\r
3053                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3054                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3055                 }\r
3056             }\r
3057         }\r
3058     }\r
3059 }\r
3060 \r
3061 /* [AS] Arrow highlighting support */\r
3062 \r
3063 static double A_WIDTH = 5; /* Width of arrow body */\r
3064 \r
3065 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3066 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3067 \r
3068 static double Sqr( double x )\r
3069 {\r
3070     return x*x;\r
3071 }\r
3072 \r
3073 static int Round( double x )\r
3074 {\r
3075     return (int) (x + 0.5);\r
3076 }\r
3077 \r
3078 /* Draw an arrow between two points using current settings */\r
3079 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3080 {\r
3081     POINT arrow[7];\r
3082     double dx, dy, j, k, x, y;\r
3083 \r
3084     if( d_x == s_x ) {\r
3085         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3086 \r
3087         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3088         arrow[0].y = s_y;\r
3089 \r
3090         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3091         arrow[1].y = d_y - h;\r
3092 \r
3093         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3094         arrow[2].y = d_y - h;\r
3095 \r
3096         arrow[3].x = d_x;\r
3097         arrow[3].y = d_y;\r
3098 \r
3099         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3100         arrow[5].y = d_y - h;\r
3101 \r
3102         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3103         arrow[4].y = d_y - h;\r
3104 \r
3105         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3106         arrow[6].y = s_y;\r
3107     }\r
3108     else if( d_y == s_y ) {\r
3109         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3110 \r
3111         arrow[0].x = s_x;\r
3112         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3113 \r
3114         arrow[1].x = d_x - w;\r
3115         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3116 \r
3117         arrow[2].x = d_x - w;\r
3118         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3119 \r
3120         arrow[3].x = d_x;\r
3121         arrow[3].y = d_y;\r
3122 \r
3123         arrow[5].x = d_x - w;\r
3124         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3125 \r
3126         arrow[4].x = d_x - w;\r
3127         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3128 \r
3129         arrow[6].x = s_x;\r
3130         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3131     }\r
3132     else {\r
3133         /* [AS] Needed a lot of paper for this! :-) */\r
3134         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3135         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3136   \r
3137         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3138 \r
3139         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3140 \r
3141         x = s_x;\r
3142         y = s_y;\r
3143 \r
3144         arrow[0].x = Round(x - j);\r
3145         arrow[0].y = Round(y + j*dx);\r
3146 \r
3147         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3148         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3149 \r
3150         if( d_x > s_x ) {\r
3151             x = (double) d_x - k;\r
3152             y = (double) d_y - k*dy;\r
3153         }\r
3154         else {\r
3155             x = (double) d_x + k;\r
3156             y = (double) d_y + k*dy;\r
3157         }\r
3158 \r
3159         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3160 \r
3161         arrow[6].x = Round(x - j);\r
3162         arrow[6].y = Round(y + j*dx);\r
3163 \r
3164         arrow[2].x = Round(arrow[6].x + 2*j);\r
3165         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3166 \r
3167         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3168         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3169 \r
3170         arrow[4].x = d_x;\r
3171         arrow[4].y = d_y;\r
3172 \r
3173         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3174         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3175     }\r
3176 \r
3177     Polygon( hdc, arrow, 7 );\r
3178 }\r
3179 \r
3180 /* [AS] Draw an arrow between two squares */\r
3181 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3182 {\r
3183     int s_x, s_y, d_x, d_y;\r
3184     HPEN hpen;\r
3185     HPEN holdpen;\r
3186     HBRUSH hbrush;\r
3187     HBRUSH holdbrush;\r
3188     LOGBRUSH stLB;\r
3189 \r
3190     if( s_col == d_col && s_row == d_row ) {\r
3191         return;\r
3192     }\r
3193 \r
3194     /* Get source and destination points */\r
3195     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3196     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3197 \r
3198     if( d_y > s_y ) {\r
3199         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3200     }\r
3201     else if( d_y < s_y ) {\r
3202         d_y += squareSize / 2 + squareSize / 4;\r
3203     }\r
3204     else {\r
3205         d_y += squareSize / 2;\r
3206     }\r
3207 \r
3208     if( d_x > s_x ) {\r
3209         d_x += squareSize / 2 - squareSize / 4;\r
3210     }\r
3211     else if( d_x < s_x ) {\r
3212         d_x += squareSize / 2 + squareSize / 4;\r
3213     }\r
3214     else {\r
3215         d_x += squareSize / 2;\r
3216     }\r
3217 \r
3218     s_x += squareSize / 2;\r
3219     s_y += squareSize / 2;\r
3220 \r
3221     /* Adjust width */\r
3222     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3223 \r
3224     /* Draw */\r
3225     stLB.lbStyle = BS_SOLID;\r
3226     stLB.lbColor = appData.highlightArrowColor;\r
3227     stLB.lbHatch = 0;\r
3228 \r
3229     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3230     holdpen = SelectObject( hdc, hpen );\r
3231     hbrush = CreateBrushIndirect( &stLB );\r
3232     holdbrush = SelectObject( hdc, hbrush );\r
3233 \r
3234     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3235 \r
3236     SelectObject( hdc, holdpen );\r
3237     SelectObject( hdc, holdbrush );\r
3238     DeleteObject( hpen );\r
3239     DeleteObject( hbrush );\r
3240 }\r
3241 \r
3242 BOOL HasHighlightInfo()\r
3243 {\r
3244     BOOL result = FALSE;\r
3245 \r
3246     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3247         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3248     {\r
3249         result = TRUE;\r
3250     }\r
3251 \r
3252     return result;\r
3253 }\r
3254 \r
3255 BOOL IsDrawArrowEnabled()\r
3256 {\r
3257     BOOL result = FALSE;\r
3258 \r
3259     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3260         result = TRUE;\r
3261     }\r
3262 \r
3263     return result;\r
3264 }\r
3265 \r
3266 VOID DrawArrowHighlight( HDC hdc )\r
3267 {\r
3268     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3269         DrawArrowBetweenSquares( hdc,\r
3270             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3271             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3272     }\r
3273 }\r
3274 \r
3275 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3276 {\r
3277     HRGN result = NULL;\r
3278 \r
3279     if( HasHighlightInfo() ) {\r
3280         int x1, y1, x2, y2;\r
3281         int sx, sy, dx, dy;\r
3282 \r
3283         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3284         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3285 \r
3286         sx = MIN( x1, x2 );\r
3287         sy = MIN( y1, y2 );\r
3288         dx = MAX( x1, x2 ) + squareSize;\r
3289         dy = MAX( y1, y2 ) + squareSize;\r
3290 \r
3291         result = CreateRectRgn( sx, sy, dx, dy );\r
3292     }\r
3293 \r
3294     return result;\r
3295 }\r
3296 \r
3297 /*\r
3298     Warning: this function modifies the behavior of several other functions. \r
3299     \r
3300     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3301     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3302     repaint is scattered all over the place, which is not good for features such as\r
3303     "arrow highlighting" that require a full repaint of the board.\r
3304 \r
3305     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3306     user interaction, when speed is not so important) but especially to avoid errors\r
3307     in the displayed graphics.\r
3308 \r
3309     In such patched places, I always try refer to this function so there is a single\r
3310     place to maintain knowledge.\r
3311     \r
3312     To restore the original behavior, just return FALSE unconditionally.\r
3313 */\r
3314 BOOL IsFullRepaintPreferrable()\r
3315 {\r
3316     BOOL result = FALSE;\r
3317 \r
3318     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3319         /* Arrow may appear on the board */\r
3320         result = TRUE;\r
3321     }\r
3322 \r
3323     return result;\r
3324 }\r
3325 \r
3326 /* \r
3327     This function is called by DrawPosition to know whether a full repaint must\r
3328     be forced or not.\r
3329 \r
3330     Only DrawPosition may directly call this function, which makes use of \r
3331     some state information. Other function should call DrawPosition specifying \r
3332     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3333 */\r
3334 BOOL DrawPositionNeedsFullRepaint()\r
3335 {\r
3336     BOOL result = FALSE;\r
3337 \r
3338     /* \r
3339         Probably a slightly better policy would be to trigger a full repaint\r
3340         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3341         but animation is fast enough that it's difficult to notice.\r
3342     */\r
3343     if( animInfo.piece == EmptySquare ) {\r
3344         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3345             result = TRUE;\r
3346         }\r
3347     }\r
3348 \r
3349     return result;\r
3350 }\r
3351 \r
3352 static HBITMAP borderBitmap;\r
3353 \r
3354 VOID\r
3355 DrawBackgroundOnDC(HDC hdc)\r
3356 {\r
3357   \r
3358   BITMAP bi;\r
3359   HDC tmphdc;\r
3360   HBITMAP hbm;\r
3361   static char oldBorder[MSG_SIZ];\r
3362   int w = 600, h = 600, mode;\r
3363 \r
3364   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3365     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3366     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3367   }\r
3368   if(borderBitmap == NULL) { // loading failed, use white\r
3369     FillRect( hdc, &boardRect, whitePieceBrush );\r
3370     return;\r
3371   }\r
3372   tmphdc = CreateCompatibleDC(hdc);\r
3373   hbm = SelectObject(tmphdc, borderBitmap);\r
3374   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3375             w = bi.bmWidth;\r
3376             h = bi.bmHeight;\r
3377   }\r
3378   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3379   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3380                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3381   SetStretchBltMode(hdc, mode);\r
3382   SelectObject(tmphdc, hbm);\r
3383   DeleteDC(tmphdc);\r
3384 }\r
3385 \r
3386 VOID\r
3387 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3388 {\r
3389   int row, column, x, y, square_color, piece_color;\r
3390   ChessSquare piece;\r
3391   HBRUSH oldBrush;\r
3392   HDC texture_hdc = NULL;\r
3393 \r
3394   /* [AS] Initialize background textures if needed */\r
3395   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3396       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3397       if( backTextureSquareSize != squareSize \r
3398        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3399           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3400           backTextureSquareSize = squareSize;\r
3401           RebuildTextureSquareInfo();\r
3402       }\r
3403 \r
3404       texture_hdc = CreateCompatibleDC( hdc );\r
3405   }\r
3406 \r
3407   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3408     for (column = 0; column < BOARD_WIDTH; column++) {\r
3409   \r
3410       SquareToPos(row, column, &x, &y);\r
3411 \r
3412       piece = board[row][column];\r
3413 \r
3414       square_color = ((column + row) % 2) == 1;\r
3415       if( gameInfo.variant == VariantXiangqi ) {\r
3416           square_color = !InPalace(row, column);\r
3417           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3418           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3419       }\r
3420       piece_color = (int) piece < (int) BlackPawn;\r
3421 \r
3422 \r
3423       /* [HGM] holdings file: light square or black */\r
3424       if(column == BOARD_LEFT-2) {\r
3425             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3426                 square_color = 1;\r
3427             else {\r
3428                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3429                 continue;\r
3430             }\r
3431       } else\r
3432       if(column == BOARD_RGHT + 1 ) {\r
3433             if( row < gameInfo.holdingsSize )\r
3434                 square_color = 1;\r
3435             else {\r
3436                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3437                 continue;\r
3438             }\r
3439       }\r
3440       if(column == BOARD_LEFT-1 ) /* left align */\r
3441             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3442       else if( column == BOARD_RGHT) /* right align */\r
3443             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3444       else\r
3445       if (appData.monoMode) {\r
3446         if (piece == EmptySquare) {\r
3447           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3448                  square_color ? WHITENESS : BLACKNESS);\r
3449         } else {\r
3450           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3451         }\r
3452       } \r
3453       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3454           /* [AS] Draw the square using a texture bitmap */\r
3455           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3456           int r = row, c = column; // [HGM] do not flip board in flipView\r
3457           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3458 \r
3459           DrawTile( x, y, \r
3460               squareSize, squareSize, \r
3461               hdc, \r
3462               texture_hdc,\r
3463               backTextureSquareInfo[r][c].mode,\r
3464               backTextureSquareInfo[r][c].x,\r
3465               backTextureSquareInfo[r][c].y );\r
3466 \r
3467           SelectObject( texture_hdc, hbm );\r
3468 \r
3469           if (piece != EmptySquare) {\r
3470               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3471           }\r
3472       }\r
3473       else {\r
3474         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3475 \r
3476         oldBrush = SelectObject(hdc, brush );\r
3477         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3478         SelectObject(hdc, oldBrush);\r
3479         if (piece != EmptySquare)\r
3480           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3481       }\r
3482     }\r
3483   }\r
3484 \r
3485   if( texture_hdc != NULL ) {\r
3486     DeleteDC( texture_hdc );\r
3487   }\r
3488 }\r
3489 \r
3490 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3491 void fputDW(FILE *f, int x)\r
3492 {\r
3493         fputc(x     & 255, f);\r
3494         fputc(x>>8  & 255, f);\r
3495         fputc(x>>16 & 255, f);\r
3496         fputc(x>>24 & 255, f);\r
3497 }\r
3498 \r
3499 #define MAX_CLIPS 200   /* more than enough */\r
3500 \r
3501 VOID\r
3502 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3503 {\r
3504 //  HBITMAP bufferBitmap;\r
3505   BITMAP bi;\r
3506 //  RECT Rect;\r
3507   HDC tmphdc;\r
3508   HBITMAP hbm;\r
3509   int w = 100, h = 50;\r
3510 \r
3511   if(logo == NULL) {\r
3512     if(!logoHeight) return;\r
3513     FillRect( hdc, &logoRect, whitePieceBrush );\r
3514   }\r
3515 //  GetClientRect(hwndMain, &Rect);\r
3516 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3517 //                                      Rect.bottom-Rect.top+1);\r
3518   tmphdc = CreateCompatibleDC(hdc);\r
3519   hbm = SelectObject(tmphdc, logo);\r
3520   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3521             w = bi.bmWidth;\r
3522             h = bi.bmHeight;\r
3523   }\r
3524   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3525                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3526   SelectObject(tmphdc, hbm);\r
3527   DeleteDC(tmphdc);\r
3528 }\r
3529 \r
3530 VOID\r
3531 DisplayLogos()\r
3532 {\r
3533   if(logoHeight) {\r
3534         HDC hdc = GetDC(hwndMain);\r
3535         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3536         if(appData.autoLogo) {\r
3537           \r
3538           switch(gameMode) { // pick logos based on game mode\r
3539             case IcsObserving:\r
3540                 whiteLogo = second.programLogo; // ICS logo\r
3541                 blackLogo = second.programLogo;\r
3542             default:\r
3543                 break;\r
3544             case IcsPlayingWhite:\r
3545                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3546                 blackLogo = second.programLogo; // ICS logo\r
3547                 break;\r
3548             case IcsPlayingBlack:\r
3549                 whiteLogo = second.programLogo; // ICS logo\r
3550                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3551                 break;\r
3552             case TwoMachinesPlay:\r
3553                 if(first.twoMachinesColor[0] == 'b') {\r
3554                     whiteLogo = second.programLogo;\r
3555                     blackLogo = first.programLogo;\r
3556                 }\r
3557                 break;\r
3558             case MachinePlaysWhite:\r
3559                 blackLogo = userLogo;\r
3560                 break;\r
3561             case MachinePlaysBlack:\r
3562                 whiteLogo = userLogo;\r
3563                 blackLogo = first.programLogo;\r
3564           }\r
3565         }\r
3566         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3567         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3568         ReleaseDC(hwndMain, hdc);\r
3569   }\r
3570 }\r
3571 \r
3572 void\r
3573 UpdateLogos(int display)\r
3574 { // called after loading new engine(s), in tourney or from menu\r
3575   LoadLogo(&first, 0, FALSE);\r
3576   LoadLogo(&second, 1, appData.icsActive);\r
3577   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3578   if(display) DisplayLogos();\r
3579 }\r
3580 \r
3581 static HDC hdcSeek;\r
3582 \r
3583 // [HGM] seekgraph\r
3584 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3585 {\r
3586     POINT stPt;\r
3587     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3588     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3589     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3590     SelectObject( hdcSeek, hp );\r
3591 }\r
3592 \r
3593 // front-end wrapper for drawing functions to do rectangles\r
3594 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3595 {\r
3596     HPEN hp;\r
3597     RECT rc;\r
3598 \r
3599     if (hdcSeek == NULL) {\r
3600     hdcSeek = GetDC(hwndMain);\r
3601       if (!appData.monoMode) {\r
3602         SelectPalette(hdcSeek, hPal, FALSE);\r
3603         RealizePalette(hdcSeek);\r
3604       }\r
3605     }\r
3606     hp = SelectObject( hdcSeek, gridPen );\r
3607     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3608     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3609     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3610     SelectObject( hdcSeek, hp );\r
3611 }\r
3612 \r
3613 // front-end wrapper for putting text in graph\r
3614 void DrawSeekText(char *buf, int x, int y)\r
3615 {\r
3616         SIZE stSize;\r
3617         SetBkMode( hdcSeek, TRANSPARENT );\r
3618         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3619         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3620 }\r
3621 \r
3622 void DrawSeekDot(int x, int y, int color)\r
3623 {\r
3624         int square = color & 0x80;\r
3625         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3626                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3627         color &= 0x7F;\r
3628         if(square)\r
3629             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3630                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3631         else\r
3632             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3633                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3634             SelectObject(hdcSeek, oldBrush);\r
3635 }\r
3636 \r
3637 void DrawSeekOpen()\r
3638 {\r
3639 }\r
3640 \r
3641 void DrawSeekClose()\r
3642 {\r
3643 }\r
3644 \r
3645 VOID\r
3646 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3647 {\r
3648   static Board lastReq[2], lastDrawn[2];\r
3649   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3650   static int lastDrawnFlipView = 0;\r
3651   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3652   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3653   HDC tmphdc;\r
3654   HDC hdcmem;\r
3655   HBITMAP bufferBitmap;\r
3656   HBITMAP oldBitmap;\r
3657   RECT Rect;\r
3658   HRGN clips[MAX_CLIPS];\r
3659   ChessSquare dragged_piece = EmptySquare;\r
3660   int nr = twoBoards*partnerUp;\r
3661 \r
3662   /* I'm undecided on this - this function figures out whether a full\r
3663    * repaint is necessary on its own, so there's no real reason to have the\r
3664    * caller tell it that.  I think this can safely be set to FALSE - but\r
3665    * if we trust the callers not to request full repaints unnessesarily, then\r
3666    * we could skip some clipping work.  In other words, only request a full\r
3667    * redraw when the majority of pieces have changed positions (ie. flip, \r
3668    * gamestart and similar)  --Hawk\r
3669    */\r
3670   Boolean fullrepaint = repaint;\r
3671 \r
3672   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3673 \r
3674   if( DrawPositionNeedsFullRepaint() ) {\r
3675       fullrepaint = TRUE;\r
3676   }\r
3677 \r
3678   if (board == NULL) {\r
3679     if (!lastReqValid[nr]) {\r
3680       return;\r
3681     }\r
3682     board = lastReq[nr];\r
3683   } else {\r
3684     CopyBoard(lastReq[nr], board);\r
3685     lastReqValid[nr] = 1;\r
3686   }\r
3687 \r
3688   if (doingSizing) {\r
3689     return;\r
3690   }\r
3691 \r
3692   if (IsIconic(hwndMain)) {\r
3693     return;\r
3694   }\r
3695 \r
3696   if (hdc == NULL) {\r
3697     hdc = GetDC(hwndMain);\r
3698     if (!appData.monoMode) {\r
3699       SelectPalette(hdc, hPal, FALSE);\r
3700       RealizePalette(hdc);\r
3701     }\r
3702     releaseDC = TRUE;\r
3703   } else {\r
3704     releaseDC = FALSE;\r
3705   }\r
3706 \r
3707   /* Create some work-DCs */\r
3708   hdcmem = CreateCompatibleDC(hdc);\r
3709   tmphdc = CreateCompatibleDC(hdc);\r
3710 \r
3711   /* If dragging is in progress, we temporarely remove the piece */\r
3712   /* [HGM] or temporarily decrease count if stacked              */\r
3713   /*       !! Moved to before board compare !!                   */\r
3714   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3715     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3716     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3717             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3718         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3719     } else \r
3720     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3721             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3722         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3723     } else \r
3724         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3725   }\r
3726 \r
3727   /* Figure out which squares need updating by comparing the \r
3728    * newest board with the last drawn board and checking if\r
3729    * flipping has changed.\r
3730    */\r
3731   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3732     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3733       for (column = 0; column < BOARD_WIDTH; column++) {\r
3734         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3735           SquareToPos(row, column, &x, &y);\r
3736           clips[num_clips++] =\r
3737             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3738         }\r
3739       }\r
3740     }\r
3741    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3742     for (i=0; i<2; i++) {\r
3743       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3744           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3745         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3746             lastDrawnHighlight.sq[i].y >= 0) {\r
3747           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3748                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3749           clips[num_clips++] =\r
3750             CreateRectRgn(x - lineGap, y - lineGap, \r
3751                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3752         }\r
3753         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3754           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3755           clips[num_clips++] =\r
3756             CreateRectRgn(x - lineGap, y - lineGap, \r
3757                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3758         }\r
3759       }\r
3760     }\r
3761     for (i=0; i<2; i++) {\r
3762       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3763           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3764         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3765             lastDrawnPremove.sq[i].y >= 0) {\r
3766           SquareToPos(lastDrawnPremove.sq[i].y,\r
3767                       lastDrawnPremove.sq[i].x, &x, &y);\r
3768           clips[num_clips++] =\r
3769             CreateRectRgn(x - lineGap, y - lineGap, \r
3770                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3771         }\r
3772         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3773             premoveHighlightInfo.sq[i].y >= 0) {\r
3774           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3775                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3776           clips[num_clips++] =\r
3777             CreateRectRgn(x - lineGap, y - lineGap, \r
3778                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3779         }\r
3780       }\r
3781     }\r
3782    } else { // nr == 1\r
3783         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3784         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3785         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3786         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3787       for (i=0; i<2; i++) {\r
3788         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3789             partnerHighlightInfo.sq[i].y >= 0) {\r
3790           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3791                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3792           clips[num_clips++] =\r
3793             CreateRectRgn(x - lineGap, y - lineGap, \r
3794                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3795         }\r
3796         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3797             oldPartnerHighlight.sq[i].y >= 0) {\r
3798           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3799                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3800           clips[num_clips++] =\r
3801             CreateRectRgn(x - lineGap, y - lineGap, \r
3802                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3803         }\r
3804       }\r
3805    }\r
3806   } else {\r
3807     fullrepaint = TRUE;\r
3808   }\r
3809 \r
3810   /* Create a buffer bitmap - this is the actual bitmap\r
3811    * being written to.  When all the work is done, we can\r
3812    * copy it to the real DC (the screen).  This avoids\r
3813    * the problems with flickering.\r
3814    */\r
3815   GetClientRect(hwndMain, &Rect);\r
3816   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3817                                         Rect.bottom-Rect.top+1);\r
3818   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3819   if (!appData.monoMode) {\r
3820     SelectPalette(hdcmem, hPal, FALSE);\r
3821   }\r
3822 \r
3823   /* Create clips for dragging */\r
3824   if (!fullrepaint) {\r
3825     if (dragInfo.from.x >= 0) {\r
3826       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3827       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3828     }\r
3829     if (dragInfo.start.x >= 0) {\r
3830       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3831       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3832     }\r
3833     if (dragInfo.pos.x >= 0) {\r
3834       x = dragInfo.pos.x - squareSize / 2;\r
3835       y = dragInfo.pos.y - squareSize / 2;\r
3836       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3837     }\r
3838     if (dragInfo.lastpos.x >= 0) {\r
3839       x = dragInfo.lastpos.x - squareSize / 2;\r
3840       y = dragInfo.lastpos.y - squareSize / 2;\r
3841       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3842     }\r
3843   }\r
3844 \r
3845   /* Are we animating a move?  \r
3846    * If so, \r
3847    *   - remove the piece from the board (temporarely)\r
3848    *   - calculate the clipping region\r
3849    */\r
3850   if (!fullrepaint) {\r
3851     if (animInfo.piece != EmptySquare) {\r
3852       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3853       x = boardRect.left + animInfo.lastpos.x;\r
3854       y = boardRect.top + animInfo.lastpos.y;\r
3855       x2 = boardRect.left + animInfo.pos.x;\r
3856       y2 = boardRect.top + animInfo.pos.y;\r
3857       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3858       /* Slight kludge.  The real problem is that after AnimateMove is\r
3859          done, the position on the screen does not match lastDrawn.\r
3860          This currently causes trouble only on e.p. captures in\r
3861          atomic, where the piece moves to an empty square and then\r
3862          explodes.  The old and new positions both had an empty square\r
3863          at the destination, but animation has drawn a piece there and\r
3864          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3865       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3866     }\r
3867   }\r
3868 \r
3869   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3870   if (num_clips == 0)\r
3871     fullrepaint = TRUE;\r
3872 \r
3873   /* Set clipping on the memory DC */\r
3874   if (!fullrepaint) {\r
3875     SelectClipRgn(hdcmem, clips[0]);\r
3876     for (x = 1; x < num_clips; x++) {\r
3877       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3878         abort();  // this should never ever happen!\r
3879     }\r
3880   }\r
3881 \r
3882   /* Do all the drawing to the memory DC */\r
3883   if(explodeInfo.radius) { // [HGM] atomic\r
3884         HBRUSH oldBrush;\r
3885         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3886         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3887         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3888         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3889         x += squareSize/2;\r
3890         y += squareSize/2;\r
3891         if(!fullrepaint) {\r
3892           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3893           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3894         }\r
3895         DrawGridOnDC(hdcmem);\r
3896         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3897         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3898         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3899         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3900         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3901         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3902         SelectObject(hdcmem, oldBrush);\r
3903   } else {\r
3904     if(border) DrawBackgroundOnDC(hdcmem);\r
3905     DrawGridOnDC(hdcmem);\r
3906     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3907         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3908         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3909     } else {\r
3910         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3911         oldPartnerHighlight = partnerHighlightInfo;\r
3912     }\r
3913     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3914   }\r
3915   if(nr == 0) // [HGM] dual: markers only on left board\r
3916   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3917     for (column = 0; column < BOARD_WIDTH; column++) {\r
3918         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3919             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3920             SquareToPos(row, column, &x, &y);\r
3921             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3922                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3923             SelectObject(hdcmem, oldBrush);\r
3924         }\r
3925     }\r
3926   }\r
3927 \r
3928   if( appData.highlightMoveWithArrow ) {\r
3929     DrawArrowHighlight(hdcmem);\r
3930   }\r
3931 \r
3932   DrawCoordsOnDC(hdcmem);\r
3933 \r
3934   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3935                  /* to make sure lastDrawn contains what is actually drawn */\r
3936 \r
3937   /* Put the dragged piece back into place and draw it (out of place!) */\r
3938     if (dragged_piece != EmptySquare) {\r
3939     /* [HGM] or restack */\r
3940     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3941                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3942     else\r
3943     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3944                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3945     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3946     x = dragInfo.pos.x - squareSize / 2;\r
3947     y = dragInfo.pos.y - squareSize / 2;\r
3948     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3949                   ((int) dragInfo.piece < (int) BlackPawn), \r
3950                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3951   }   \r
3952   \r
3953   /* Put the animated piece back into place and draw it */\r
3954   if (animInfo.piece != EmptySquare) {\r
3955     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3956     x = boardRect.left + animInfo.pos.x;\r
3957     y = boardRect.top + animInfo.pos.y;\r
3958     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3959                   ((int) animInfo.piece < (int) BlackPawn),\r
3960                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3961   }\r
3962 \r
3963   /* Release the bufferBitmap by selecting in the old bitmap \r
3964    * and delete the memory DC\r
3965    */\r
3966   SelectObject(hdcmem, oldBitmap);\r
3967   DeleteDC(hdcmem);\r
3968 \r
3969   /* Set clipping on the target DC */\r
3970   if (!fullrepaint) {\r
3971     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3972         RECT rect;\r
3973         GetRgnBox(clips[x], &rect);\r
3974         DeleteObject(clips[x]);\r
3975         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3976                           rect.right + wpMain.width/2, rect.bottom);\r
3977     }\r
3978     SelectClipRgn(hdc, clips[0]);\r
3979     for (x = 1; x < num_clips; x++) {\r
3980       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3981         abort();   // this should never ever happen!\r
3982     } \r
3983   }\r
3984 \r
3985   /* Copy the new bitmap onto the screen in one go.\r
3986    * This way we avoid any flickering\r
3987    */\r
3988   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3989   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3990          boardRect.right - boardRect.left,\r
3991          boardRect.bottom - boardRect.top,\r
3992          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3993   if(saveDiagFlag) { \r
3994     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3995     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3996 \r
3997     GetObject(bufferBitmap, sizeof(b), &b);\r
3998     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3999         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4000         bih.biWidth = b.bmWidth;\r
4001         bih.biHeight = b.bmHeight;\r
4002         bih.biPlanes = 1;\r
4003         bih.biBitCount = b.bmBitsPixel;\r
4004         bih.biCompression = 0;\r
4005         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4006         bih.biXPelsPerMeter = 0;\r
4007         bih.biYPelsPerMeter = 0;\r
4008         bih.biClrUsed = 0;\r
4009         bih.biClrImportant = 0;\r
4010 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4011 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4012         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4013 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4014 \r
4015         wb = b.bmWidthBytes;\r
4016         // count colors\r
4017         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4018                 int k = ((int*) pData)[i];\r
4019                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4020                 if(j >= 16) break;\r
4021                 color[j] = k;\r
4022                 if(j >= nrColors) nrColors = j+1;\r
4023         }\r
4024         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4025                 INT p = 0;\r
4026                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4027                     for(w=0; w<(wb>>2); w+=2) {\r
4028                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4029                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4030                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4031                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4032                         pData[p++] = m | j<<4;\r
4033                     }\r
4034                     while(p&3) pData[p++] = 0;\r
4035                 }\r
4036                 fac = 3;\r
4037                 wb = ((wb+31)>>5)<<2;\r
4038         }\r
4039         // write BITMAPFILEHEADER\r
4040         fprintf(diagFile, "BM");\r
4041         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4042         fputDW(diagFile, 0);\r
4043         fputDW(diagFile, 0x36 + (fac?64:0));\r
4044         // write BITMAPINFOHEADER\r
4045         fputDW(diagFile, 40);\r
4046         fputDW(diagFile, b.bmWidth);\r
4047         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4048         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4049         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4050         fputDW(diagFile, 0);\r
4051         fputDW(diagFile, 0);\r
4052         fputDW(diagFile, 0);\r
4053         fputDW(diagFile, 0);\r
4054         fputDW(diagFile, 0);\r
4055         fputDW(diagFile, 0);\r
4056         // write color table\r
4057         if(fac)\r
4058         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4059         // write bitmap data\r
4060         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4061                 fputc(pData[i], diagFile);\r
4062         free(pData);\r
4063      }\r
4064   }\r
4065 \r
4066   SelectObject(tmphdc, oldBitmap);\r
4067 \r
4068   /* Massive cleanup */\r
4069   for (x = 0; x < num_clips; x++)\r
4070     DeleteObject(clips[x]);\r
4071 \r
4072   DeleteDC(tmphdc);\r
4073   DeleteObject(bufferBitmap);\r
4074 \r
4075   if (releaseDC) \r
4076     ReleaseDC(hwndMain, hdc);\r
4077   \r
4078   if (lastDrawnFlipView != flipView && nr == 0) {\r
4079     if (flipView)\r
4080       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4081     else\r
4082       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4083   }\r
4084 \r
4085 /*  CopyBoard(lastDrawn, board);*/\r
4086   lastDrawnHighlight = highlightInfo;\r
4087   lastDrawnPremove   = premoveHighlightInfo;\r
4088   lastDrawnFlipView = flipView;\r
4089   lastDrawnValid[nr] = 1;\r
4090 }\r
4091 \r
4092 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4093 int\r
4094 SaveDiagram(f)\r
4095      FILE *f;\r
4096 {\r
4097     saveDiagFlag = 1; diagFile = f;\r
4098     HDCDrawPosition(NULL, TRUE, NULL);\r
4099     saveDiagFlag = 0;\r
4100 \r
4101     fclose(f);\r
4102     return TRUE;\r
4103 }\r
4104 \r
4105 \r
4106 /*---------------------------------------------------------------------------*\\r
4107 | CLIENT PAINT PROCEDURE\r
4108 |   This is the main event-handler for the WM_PAINT message.\r
4109 |\r
4110 \*---------------------------------------------------------------------------*/\r
4111 VOID\r
4112 PaintProc(HWND hwnd)\r
4113 {\r
4114   HDC         hdc;\r
4115   PAINTSTRUCT ps;\r
4116   HFONT       oldFont;\r
4117 \r
4118   if((hdc = BeginPaint(hwnd, &ps))) {\r
4119     if (IsIconic(hwnd)) {\r
4120       DrawIcon(hdc, 2, 2, iconCurrent);\r
4121     } else {\r
4122       if (!appData.monoMode) {\r
4123         SelectPalette(hdc, hPal, FALSE);\r
4124         RealizePalette(hdc);\r
4125       }\r
4126       HDCDrawPosition(hdc, 1, NULL);\r
4127       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4128         flipView = !flipView; partnerUp = !partnerUp;\r
4129         HDCDrawPosition(hdc, 1, NULL);\r
4130         flipView = !flipView; partnerUp = !partnerUp;\r
4131       }\r
4132       oldFont =\r
4133         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4134       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4135                  ETO_CLIPPED|ETO_OPAQUE,\r
4136                  &messageRect, messageText, strlen(messageText), NULL);\r
4137       SelectObject(hdc, oldFont);\r
4138       DisplayBothClocks();\r
4139       DisplayLogos();\r
4140     }\r
4141     EndPaint(hwnd,&ps);\r
4142   }\r
4143 \r
4144   return;\r
4145 }\r
4146 \r
4147 \r
4148 /*\r
4149  * If the user selects on a border boundary, return -1; if off the board,\r
4150  *   return -2.  Otherwise map the event coordinate to the square.\r
4151  * The offset boardRect.left or boardRect.top must already have been\r
4152  *   subtracted from x.\r
4153  */\r
4154 int EventToSquare(x, limit)\r
4155      int x, limit;\r
4156 {\r
4157   if (x <= border)\r
4158     return -2;\r
4159   if (x < lineGap + border)\r
4160     return -1;\r
4161   x -= lineGap + border;\r
4162   if ((x % (squareSize + lineGap)) >= squareSize)\r
4163     return -1;\r
4164   x /= (squareSize + lineGap);\r
4165     if (x >= limit)\r
4166     return -2;\r
4167   return x;\r
4168 }\r
4169 \r
4170 typedef struct {\r
4171   char piece;\r
4172   int command;\r
4173   char* name;\r
4174 } DropEnable;\r
4175 \r
4176 DropEnable dropEnables[] = {\r
4177   { 'P', DP_Pawn, N_("Pawn") },\r
4178   { 'N', DP_Knight, N_("Knight") },\r
4179   { 'B', DP_Bishop, N_("Bishop") },\r
4180   { 'R', DP_Rook, N_("Rook") },\r
4181   { 'Q', DP_Queen, N_("Queen") },\r
4182 };\r
4183 \r
4184 VOID\r
4185 SetupDropMenu(HMENU hmenu)\r
4186 {\r
4187   int i, count, enable;\r
4188   char *p;\r
4189   extern char white_holding[], black_holding[];\r
4190   char item[MSG_SIZ];\r
4191 \r
4192   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4193     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4194                dropEnables[i].piece);\r
4195     count = 0;\r
4196     while (p && *p++ == dropEnables[i].piece) count++;\r
4197       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4198     enable = count > 0 || !appData.testLegality\r
4199       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4200                       && !appData.icsActive);\r
4201     ModifyMenu(hmenu, dropEnables[i].command,\r
4202                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4203                dropEnables[i].command, item);\r
4204   }\r
4205 }\r
4206 \r
4207 void DragPieceBegin(int x, int y, Boolean instantly)\r
4208 {\r
4209       dragInfo.lastpos.x = boardRect.left + x;\r
4210       dragInfo.lastpos.y = boardRect.top + y;\r
4211       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4212       dragInfo.from.x = fromX;\r
4213       dragInfo.from.y = fromY;\r
4214       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4215       dragInfo.start = dragInfo.from;\r
4216       SetCapture(hwndMain);\r
4217 }\r
4218 \r
4219 void DragPieceEnd(int x, int y)\r
4220 {\r
4221     ReleaseCapture();\r
4222     dragInfo.start.x = dragInfo.start.y = -1;\r
4223     dragInfo.from = dragInfo.start;\r
4224     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4225 }\r
4226 \r
4227 void ChangeDragPiece(ChessSquare piece)\r
4228 {\r
4229     dragInfo.piece = piece;\r
4230 }\r
4231 \r
4232 /* Event handler for mouse messages */\r
4233 VOID\r
4234 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4235 {\r
4236   int x, y, menuNr;\r
4237   POINT pt;\r
4238   static int recursive = 0;\r
4239   HMENU hmenu;\r
4240   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4241 \r
4242   if (recursive) {\r
4243     if (message == WM_MBUTTONUP) {\r
4244       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4245          to the middle button: we simulate pressing the left button too!\r
4246          */\r
4247       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4248       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4249     }\r
4250     return;\r
4251   }\r
4252   recursive++;\r
4253   \r
4254   pt.x = LOWORD(lParam);\r
4255   pt.y = HIWORD(lParam);\r
4256   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4257   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4258   if (!flipView && y >= 0) {\r
4259     y = BOARD_HEIGHT - 1 - y;\r
4260   }\r
4261   if (flipView && x >= 0) {\r
4262     x = BOARD_WIDTH - 1 - x;\r
4263   }\r
4264 \r
4265   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4266   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4267 \r
4268   switch (message) {\r
4269   case WM_LBUTTONDOWN:\r
4270       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4271         ClockClick(flipClock); break;\r
4272       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4273         ClockClick(!flipClock); break;\r
4274       }\r
4275       dragInfo.start.x = dragInfo.start.y = -1;\r
4276       dragInfo.from = dragInfo.start;\r
4277     if(fromX == -1 && frozen) { // not sure where this is for\r
4278                 fromX = fromY = -1; \r
4279       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4280       break;\r
4281     }\r
4282       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4283       DrawPosition(TRUE, NULL);\r
4284     break;\r
4285 \r
4286   case WM_LBUTTONUP:\r
4287       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4288       DrawPosition(TRUE, NULL);\r
4289     break;\r
4290 \r
4291   case WM_MOUSEMOVE:\r
4292     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4293     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4294     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4295     if ((appData.animateDragging || appData.highlightDragging)\r
4296         && (wParam & MK_LBUTTON)\r
4297         && dragInfo.from.x >= 0) \r
4298     {\r
4299       BOOL full_repaint = FALSE;\r
4300 \r
4301       if (appData.animateDragging) {\r
4302         dragInfo.pos = pt;\r
4303       }\r
4304       if (appData.highlightDragging) {\r
4305         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4306         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4307             full_repaint = TRUE;\r
4308         }\r
4309       }\r
4310       \r
4311       DrawPosition( full_repaint, NULL);\r
4312       \r
4313       dragInfo.lastpos = dragInfo.pos;\r
4314     }\r
4315     break;\r
4316 \r
4317   case WM_MOUSEWHEEL: // [DM]\r
4318     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4319        /* Mouse Wheel is being rolled forward\r
4320         * Play moves forward\r
4321         */\r
4322        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4323                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4324        /* Mouse Wheel is being rolled backward\r
4325         * Play moves backward\r
4326         */\r
4327        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4328                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4329     }\r
4330     break;\r
4331 \r
4332   case WM_MBUTTONUP:\r
4333   case WM_RBUTTONUP:\r
4334     ReleaseCapture();\r
4335     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4336     break;\r
4337  \r
4338   case WM_MBUTTONDOWN:\r
4339   case WM_RBUTTONDOWN:\r
4340     ErrorPopDown();\r
4341     ReleaseCapture();\r
4342     fromX = fromY = -1;\r
4343     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4344     dragInfo.start.x = dragInfo.start.y = -1;\r
4345     dragInfo.from = dragInfo.start;\r
4346     dragInfo.lastpos = dragInfo.pos;\r
4347     if (appData.highlightDragging) {\r
4348       ClearHighlights();\r
4349     }\r
4350     if(y == -2) {\r
4351       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4352       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4353           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4354       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4355           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4356       }\r
4357       break;\r
4358     }\r
4359     DrawPosition(TRUE, NULL);\r
4360 \r
4361     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4362     switch (menuNr) {\r
4363     case 0:\r
4364       if (message == WM_MBUTTONDOWN) {\r
4365         buttonCount = 3;  /* even if system didn't think so */\r
4366         if (wParam & MK_SHIFT) \r
4367           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4368         else\r
4369           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4370       } else { /* message == WM_RBUTTONDOWN */\r
4371         /* Just have one menu, on the right button.  Windows users don't\r
4372            think to try the middle one, and sometimes other software steals\r
4373            it, or it doesn't really exist. */\r
4374         if(gameInfo.variant != VariantShogi)\r
4375             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4376         else\r
4377             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4378       }\r
4379       break;\r
4380     case 2:\r
4381       SetCapture(hwndMain);\r
4382       break;\r
4383     case 1:\r
4384       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4385       SetupDropMenu(hmenu);\r
4386       MenuPopup(hwnd, pt, hmenu, -1);\r
4387     default:\r
4388       break;\r
4389     }\r
4390     break;\r
4391   }\r
4392 \r
4393   recursive--;\r
4394 }\r
4395 \r
4396 /* Preprocess messages for buttons in main window */\r
4397 LRESULT CALLBACK\r
4398 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4399 {\r
4400   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4401   int i, dir;\r
4402 \r
4403   for (i=0; i<N_BUTTONS; i++) {\r
4404     if (buttonDesc[i].id == id) break;\r
4405   }\r
4406   if (i == N_BUTTONS) return 0;\r
4407   switch (message) {\r
4408   case WM_KEYDOWN:\r
4409     switch (wParam) {\r
4410     case VK_LEFT:\r
4411     case VK_RIGHT:\r
4412       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4413       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4414       return TRUE;\r
4415     }\r
4416     break;\r
4417   case WM_CHAR:\r
4418     switch (wParam) {\r
4419     case '\r':\r
4420       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4421       return TRUE;\r
4422     default:\r
4423       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4424         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4425         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4426         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4427         SetFocus(h);\r
4428         SendMessage(h, WM_CHAR, wParam, lParam);\r
4429         return TRUE;\r
4430       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4431         TypeInEvent((char)wParam);\r
4432       }\r
4433       break;\r
4434     }\r
4435     break;\r
4436   }\r
4437   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4438 }\r
4439 \r
4440 /* Process messages for Promotion dialog box */\r
4441 LRESULT CALLBACK\r
4442 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4443 {\r
4444   char promoChar;\r
4445 \r
4446   switch (message) {\r
4447   case WM_INITDIALOG: /* message: initialize dialog box */\r
4448     /* Center the dialog over the application window */\r
4449     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4450     Translate(hDlg, DLG_PromotionKing);\r
4451     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4452       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4453        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4454        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4455                SW_SHOW : SW_HIDE);\r
4456     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4457     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4458        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4459          PieceToChar(WhiteAngel) != '~') ||\r
4460         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4461          PieceToChar(BlackAngel) != '~')   ) ?\r
4462                SW_SHOW : SW_HIDE);\r
4463     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4464        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4465          PieceToChar(WhiteMarshall) != '~') ||\r
4466         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4467          PieceToChar(BlackMarshall) != '~')   ) ?\r
4468                SW_SHOW : SW_HIDE);\r
4469     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4470     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4471        gameInfo.variant != VariantShogi ?\r
4472                SW_SHOW : SW_HIDE);\r
4473     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4474        gameInfo.variant != VariantShogi ?\r
4475                SW_SHOW : SW_HIDE);\r
4476     if(gameInfo.variant == VariantShogi) {\r
4477         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4478         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4479         SetWindowText(hDlg, "Promote?");\r
4480     }\r
4481     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4482        gameInfo.variant == VariantSuper ?\r
4483                SW_SHOW : SW_HIDE);\r
4484     return TRUE;\r
4485 \r
4486   case WM_COMMAND: /* message: received a command */\r
4487     switch (LOWORD(wParam)) {\r
4488     case IDCANCEL:\r
4489       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4490       ClearHighlights();\r
4491       DrawPosition(FALSE, NULL);\r
4492       return TRUE;\r
4493     case PB_King:\r
4494       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4495       break;\r
4496     case PB_Queen:\r
4497       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4498       break;\r
4499     case PB_Rook:\r
4500       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4501       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4502       break;\r
4503     case PB_Bishop:\r
4504       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4505       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4506       break;\r
4507     case PB_Chancellor:\r
4508       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4509       break;\r
4510     case PB_Archbishop:\r
4511       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4512       break;\r
4513     case PB_Knight:\r
4514       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4515       break;\r
4516     default:\r
4517       return FALSE;\r
4518     }\r
4519     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4520     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4521     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4522     fromX = fromY = -1;\r
4523     if (!appData.highlightLastMove) {\r
4524       ClearHighlights();\r
4525       DrawPosition(FALSE, NULL);\r
4526     }\r
4527     return TRUE;\r
4528   }\r
4529   return FALSE;\r
4530 }\r
4531 \r
4532 /* Pop up promotion dialog */\r
4533 VOID\r
4534 PromotionPopup(HWND hwnd)\r
4535 {\r
4536   FARPROC lpProc;\r
4537 \r
4538   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4539   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4540     hwnd, (DLGPROC)lpProc);\r
4541   FreeProcInstance(lpProc);\r
4542 }\r
4543 \r
4544 void\r
4545 PromotionPopUp()\r
4546 {\r
4547   DrawPosition(TRUE, NULL);\r
4548   PromotionPopup(hwndMain);\r
4549 }\r
4550 \r
4551 VOID\r
4552 LoadGameDialog(HWND hwnd, char* title)\r
4553 {\r
4554   UINT number = 0;\r
4555   FILE *f;\r
4556   char fileTitle[MSG_SIZ];\r
4557   f = OpenFileDialog(hwnd, "rb", "",\r
4558                      appData.oldSaveStyle ? "gam" : "pgn",\r
4559                      GAME_FILT,\r
4560                      title, &number, fileTitle, NULL);\r
4561   if (f != NULL) {\r
4562     cmailMsgLoaded = FALSE;\r
4563     if (number == 0) {\r
4564       int error = GameListBuild(f);\r
4565       if (error) {\r
4566         DisplayError(_("Cannot build game list"), error);\r
4567       } else if (!ListEmpty(&gameList) &&\r
4568                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4569         GameListPopUp(f, fileTitle);\r
4570         return;\r
4571       }\r
4572       GameListDestroy();\r
4573       number = 1;\r
4574     }\r
4575     LoadGame(f, number, fileTitle, FALSE);\r
4576   }\r
4577 }\r
4578 \r
4579 int get_term_width()\r
4580 {\r
4581     HDC hdc;\r
4582     TEXTMETRIC tm;\r
4583     RECT rc;\r
4584     HFONT hfont, hold_font;\r
4585     LOGFONT lf;\r
4586     HWND hText;\r
4587 \r
4588     if (hwndConsole)\r
4589         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4590     else\r
4591         return 79;\r
4592 \r
4593     // get the text metrics\r
4594     hdc = GetDC(hText);\r
4595     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4596     if (consoleCF.dwEffects & CFE_BOLD)\r
4597         lf.lfWeight = FW_BOLD;\r
4598     if (consoleCF.dwEffects & CFE_ITALIC)\r
4599         lf.lfItalic = TRUE;\r
4600     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4601         lf.lfStrikeOut = TRUE;\r
4602     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4603         lf.lfUnderline = TRUE;\r
4604     hfont = CreateFontIndirect(&lf);\r
4605     hold_font = SelectObject(hdc, hfont);\r
4606     GetTextMetrics(hdc, &tm);\r
4607     SelectObject(hdc, hold_font);\r
4608     DeleteObject(hfont);\r
4609     ReleaseDC(hText, hdc);\r
4610 \r
4611     // get the rectangle\r
4612     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4613 \r
4614     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4615 }\r
4616 \r
4617 void UpdateICSWidth(HWND hText)\r
4618 {\r
4619     LONG old_width, new_width;\r
4620 \r
4621     new_width = get_term_width(hText, FALSE);\r
4622     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4623     if (new_width != old_width)\r
4624     {\r
4625         ics_update_width(new_width);\r
4626         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4627     }\r
4628 }\r
4629 \r
4630 VOID\r
4631 ChangedConsoleFont()\r
4632 {\r
4633   CHARFORMAT cfmt;\r
4634   CHARRANGE tmpsel, sel;\r
4635   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4636   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4637   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4638   PARAFORMAT paraf;\r
4639 \r
4640   cfmt.cbSize = sizeof(CHARFORMAT);\r
4641   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4642     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4643                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4644   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4645    * size.  This was undocumented in the version of MSVC++ that I had\r
4646    * when I wrote the code, but is apparently documented now.\r
4647    */\r
4648   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4649   cfmt.bCharSet = f->lf.lfCharSet;\r
4650   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4651   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4652   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4653   /* Why are the following seemingly needed too? */\r
4654   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4655   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4656   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4657   tmpsel.cpMin = 0;\r
4658   tmpsel.cpMax = -1; /*999999?*/\r
4659   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4660   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4661   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4662    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4663    */\r
4664   paraf.cbSize = sizeof(paraf);\r
4665   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4666   paraf.dxStartIndent = 0;\r
4667   paraf.dxOffset = WRAP_INDENT;\r
4668   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4669   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4670   UpdateICSWidth(hText);\r
4671 }\r
4672 \r
4673 /*---------------------------------------------------------------------------*\\r
4674  *\r
4675  * Window Proc for main window\r
4676  *\r
4677 \*---------------------------------------------------------------------------*/\r
4678 \r
4679 /* Process messages for main window, etc. */\r
4680 LRESULT CALLBACK\r
4681 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4682 {\r
4683   FARPROC lpProc;\r
4684   int wmId, wmEvent;\r
4685   char *defName;\r
4686   FILE *f;\r
4687   UINT number;\r
4688   char fileTitle[MSG_SIZ];\r
4689   static SnapData sd;\r
4690   static int peek=0;\r
4691 \r
4692   switch (message) {\r
4693 \r
4694   case WM_PAINT: /* message: repaint portion of window */\r
4695     PaintProc(hwnd);\r
4696     break;\r
4697 \r
4698   case WM_ERASEBKGND:\r
4699     if (IsIconic(hwnd)) {\r
4700       /* Cheat; change the message */\r
4701       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4702     } else {\r
4703       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4704     }\r
4705     break;\r
4706 \r
4707   case WM_LBUTTONDOWN:\r
4708   case WM_MBUTTONDOWN:\r
4709   case WM_RBUTTONDOWN:\r
4710   case WM_LBUTTONUP:\r
4711   case WM_MBUTTONUP:\r
4712   case WM_RBUTTONUP:\r
4713   case WM_MOUSEMOVE:\r
4714   case WM_MOUSEWHEEL:\r
4715     MouseEvent(hwnd, message, wParam, lParam);\r
4716     break;\r
4717 \r
4718   case WM_KEYUP:\r
4719     if((char)wParam == '\b') {\r
4720       ForwardEvent(); peek = 0;\r
4721     }\r
4722 \r
4723     JAWS_KBUP_NAVIGATION\r
4724 \r
4725     break;\r
4726 \r
4727   case WM_KEYDOWN:\r
4728     if((char)wParam == '\b') {\r
4729       if(!peek) BackwardEvent(), peek = 1;\r
4730     }\r
4731 \r
4732     JAWS_KBDOWN_NAVIGATION\r
4733 \r
4734     break;\r
4735 \r
4736   case WM_CHAR:\r
4737     \r
4738     JAWS_ALT_INTERCEPT\r
4739 \r
4740     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4741         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4742         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4743         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4744         SetFocus(h);\r
4745         SendMessage(h, message, wParam, lParam);\r
4746     } else if(lParam != KF_REPEAT) {\r
4747         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4748                 TypeInEvent((char)wParam);\r
4749         } else if((char)wParam == 003) CopyGameToClipboard();\r
4750          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4751     }\r
4752 \r
4753     break;\r
4754 \r
4755   case WM_PALETTECHANGED:\r
4756     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4757       int nnew;\r
4758       HDC hdc = GetDC(hwndMain);\r
4759       SelectPalette(hdc, hPal, TRUE);\r
4760       nnew = RealizePalette(hdc);\r
4761       if (nnew > 0) {\r
4762         paletteChanged = TRUE;\r
4763 \r
4764         InvalidateRect(hwnd, &boardRect, FALSE);\r
4765       }\r
4766       ReleaseDC(hwnd, hdc);\r
4767     }\r
4768     break;\r
4769 \r
4770   case WM_QUERYNEWPALETTE:\r
4771     if (!appData.monoMode /*&& paletteChanged*/) {\r
4772       int nnew;\r
4773       HDC hdc = GetDC(hwndMain);\r
4774       paletteChanged = FALSE;\r
4775       SelectPalette(hdc, hPal, FALSE);\r
4776       nnew = RealizePalette(hdc);\r
4777       if (nnew > 0) {\r
4778         InvalidateRect(hwnd, &boardRect, FALSE);\r
4779       }\r
4780       ReleaseDC(hwnd, hdc);\r
4781       return TRUE;\r
4782     }\r
4783     return FALSE;\r
4784 \r
4785   case WM_COMMAND: /* message: command from application menu */\r
4786     wmId    = LOWORD(wParam);\r
4787     wmEvent = HIWORD(wParam);\r
4788 \r
4789     switch (wmId) {\r
4790     case IDM_NewGame:\r
4791       ResetGameEvent();\r
4792       SAY("new game enter a move to play against the computer with white");\r
4793       break;\r
4794 \r
4795     case IDM_NewGameFRC:\r
4796       if( NewGameFRC() == 0 ) {\r
4797         ResetGameEvent();\r
4798       }\r
4799       break;\r
4800 \r
4801     case IDM_NewVariant:\r
4802       NewVariantPopup(hwnd);\r
4803       break;\r
4804 \r
4805     case IDM_LoadGame:\r
4806       LoadGameDialog(hwnd, _("Load Game from File"));\r
4807       break;\r
4808 \r
4809     case IDM_LoadNextGame:\r
4810       ReloadGame(1);\r
4811       break;\r
4812 \r
4813     case IDM_LoadPrevGame:\r
4814       ReloadGame(-1);\r
4815       break;\r
4816 \r
4817     case IDM_ReloadGame:\r
4818       ReloadGame(0);\r
4819       break;\r
4820 \r
4821     case IDM_LoadPosition:\r
4822       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4823         Reset(FALSE, TRUE);\r
4824       }\r
4825       number = 1;\r
4826       f = OpenFileDialog(hwnd, "rb", "",\r
4827                          appData.oldSaveStyle ? "pos" : "fen",\r
4828                          POSITION_FILT,\r
4829                          _("Load Position from File"), &number, fileTitle, NULL);\r
4830       if (f != NULL) {\r
4831         LoadPosition(f, number, fileTitle);\r
4832       }\r
4833       break;\r
4834 \r
4835     case IDM_LoadNextPosition:\r
4836       ReloadPosition(1);\r
4837       break;\r
4838 \r
4839     case IDM_LoadPrevPosition:\r
4840       ReloadPosition(-1);\r
4841       break;\r
4842 \r
4843     case IDM_ReloadPosition:\r
4844       ReloadPosition(0);\r
4845       break;\r
4846 \r
4847     case IDM_SaveGame:\r
4848       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4849       f = OpenFileDialog(hwnd, "a", defName,\r
4850                          appData.oldSaveStyle ? "gam" : "pgn",\r
4851                          GAME_FILT,\r
4852                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4853       if (f != NULL) {\r
4854         SaveGame(f, 0, "");\r
4855       }\r
4856       break;\r
4857 \r
4858     case IDM_SavePosition:\r
4859       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4860       f = OpenFileDialog(hwnd, "a", defName,\r
4861                          appData.oldSaveStyle ? "pos" : "fen",\r
4862                          POSITION_FILT,\r
4863                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4864       if (f != NULL) {\r
4865         SavePosition(f, 0, "");\r
4866       }\r
4867       break;\r
4868 \r
4869     case IDM_SaveDiagram:\r
4870       defName = "diagram";\r
4871       f = OpenFileDialog(hwnd, "wb", defName,\r
4872                          "bmp",\r
4873                          DIAGRAM_FILT,\r
4874                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4875       if (f != NULL) {\r
4876         SaveDiagram(f);\r
4877       }\r
4878       break;\r
4879 \r
4880     case IDM_CreateBook:\r
4881       CreateBookEvent();\r
4882       break;\r
4883 \r
4884     case IDM_CopyGame:\r
4885       CopyGameToClipboard();\r
4886       break;\r
4887 \r
4888     case IDM_PasteGame:\r
4889       PasteGameFromClipboard();\r
4890       break;\r
4891 \r
4892     case IDM_CopyGameListToClipboard:\r
4893       CopyGameListToClipboard();\r
4894       break;\r
4895 \r
4896     /* [AS] Autodetect FEN or PGN data */\r
4897     case IDM_PasteAny:\r
4898       PasteGameOrFENFromClipboard();\r
4899       break;\r
4900 \r
4901     /* [AS] Move history */\r
4902     case IDM_ShowMoveHistory:\r
4903         if( MoveHistoryIsUp() ) {\r
4904             MoveHistoryPopDown();\r
4905         }\r
4906         else {\r
4907             MoveHistoryPopUp();\r
4908         }\r
4909         break;\r
4910 \r
4911     /* [AS] Eval graph */\r
4912     case IDM_ShowEvalGraph:\r
4913         if( EvalGraphIsUp() ) {\r
4914             EvalGraphPopDown();\r
4915         }\r
4916         else {\r
4917             EvalGraphPopUp();\r
4918             SetFocus(hwndMain);\r
4919         }\r
4920         break;\r
4921 \r
4922     /* [AS] Engine output */\r
4923     case IDM_ShowEngineOutput:\r
4924         if( EngineOutputIsUp() ) {\r
4925             EngineOutputPopDown();\r
4926         }\r
4927         else {\r
4928             EngineOutputPopUp();\r
4929         }\r
4930         break;\r
4931 \r
4932     /* [AS] User adjudication */\r
4933     case IDM_UserAdjudication_White:\r
4934         UserAdjudicationEvent( +1 );\r
4935         break;\r
4936 \r
4937     case IDM_UserAdjudication_Black:\r
4938         UserAdjudicationEvent( -1 );\r
4939         break;\r
4940 \r
4941     case IDM_UserAdjudication_Draw:\r
4942         UserAdjudicationEvent( 0 );\r
4943         break;\r
4944 \r
4945     /* [AS] Game list options dialog */\r
4946     case IDM_GameListOptions:\r
4947       GameListOptions();\r
4948       break;\r
4949 \r
4950     case IDM_NewChat:\r
4951       ChatPopUp(NULL);\r
4952       break;\r
4953 \r
4954     case IDM_CopyPosition:\r
4955       CopyFENToClipboard();\r
4956       break;\r
4957 \r
4958     case IDM_PastePosition:\r
4959       PasteFENFromClipboard();\r
4960       break;\r
4961 \r
4962     case IDM_MailMove:\r
4963       MailMoveEvent();\r
4964       break;\r
4965 \r
4966     case IDM_ReloadCMailMsg:\r
4967       Reset(TRUE, TRUE);\r
4968       ReloadCmailMsgEvent(FALSE);\r
4969       break;\r
4970 \r
4971     case IDM_Minimize:\r
4972       ShowWindow(hwnd, SW_MINIMIZE);\r
4973       break;\r
4974 \r
4975     case IDM_Exit:\r
4976       ExitEvent(0);\r
4977       break;\r
4978 \r
4979     case IDM_MachineWhite:\r
4980       MachineWhiteEvent();\r
4981       /*\r
4982        * refresh the tags dialog only if it's visible\r
4983        */\r
4984       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4985           char *tags;\r
4986           tags = PGNTags(&gameInfo);\r
4987           TagsPopUp(tags, CmailMsg());\r
4988           free(tags);\r
4989       }\r
4990       SAY("computer starts playing white");\r
4991       break;\r
4992 \r
4993     case IDM_MachineBlack:\r
4994       MachineBlackEvent();\r
4995       /*\r
4996        * refresh the tags dialog only if it's visible\r
4997        */\r
4998       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4999           char *tags;\r
5000           tags = PGNTags(&gameInfo);\r
5001           TagsPopUp(tags, CmailMsg());\r
5002           free(tags);\r
5003       }\r
5004       SAY("computer starts playing black");\r
5005       break;\r
5006 \r
5007     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5008       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5009       break;\r
5010 \r
5011     case IDM_TwoMachines:\r
5012       TwoMachinesEvent();\r
5013       /*\r
5014        * refresh the tags dialog only if it's visible\r
5015        */\r
5016       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5017           char *tags;\r
5018           tags = PGNTags(&gameInfo);\r
5019           TagsPopUp(tags, CmailMsg());\r
5020           free(tags);\r
5021       }\r
5022       SAY("computer starts playing both sides");\r
5023       break;\r
5024 \r
5025     case IDM_AnalysisMode:\r
5026       if(AnalyzeModeEvent()) {\r
5027         SAY("analyzing current position");\r
5028       }\r
5029       break;\r
5030 \r
5031     case IDM_AnalyzeFile:\r
5032       AnalyzeFileEvent();\r
5033       break;\r
5034 \r
5035     case IDM_IcsClient:\r
5036       IcsClientEvent();\r
5037       break;\r
5038 \r
5039     case IDM_EditGame:\r
5040     case IDM_EditGame2:\r
5041       EditGameEvent();\r
5042       SAY("edit game");\r
5043       break;\r
5044 \r
5045     case IDM_EditPosition:\r
5046     case IDM_EditPosition2:\r
5047       EditPositionEvent();\r
5048       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5049       break;\r
5050 \r
5051     case IDM_Training:\r
5052       TrainingEvent();\r
5053       break;\r
5054 \r
5055     case IDM_ShowGameList:\r
5056       ShowGameListProc();\r
5057       break;\r
5058 \r
5059     case IDM_EditProgs1:\r
5060       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5061       break;\r
5062 \r
5063     case IDM_LoadProg1:\r
5064      LoadEnginePopUp(hwndMain, 0);\r
5065       break;\r
5066 \r
5067     case IDM_LoadProg2:\r
5068      LoadEnginePopUp(hwndMain, 1);\r
5069       break;\r
5070 \r
5071     case IDM_EditServers:\r
5072       EditTagsPopUp(icsNames, &icsNames);\r
5073       break;\r
5074 \r
5075     case IDM_EditTags:\r
5076     case IDM_Tags:\r
5077       EditTagsProc();\r
5078       break;\r
5079 \r
5080     case IDM_EditBook:\r
5081       EditBookEvent();\r
5082       break;\r
5083 \r
5084     case IDM_EditComment:\r
5085     case IDM_Comment:\r
5086       if (commentUp && editComment) {\r
5087         CommentPopDown();\r
5088       } else {\r
5089         EditCommentEvent();\r
5090       }\r
5091       break;\r
5092 \r
5093     case IDM_Pause:\r
5094       PauseEvent();\r
5095       break;\r
5096 \r
5097     case IDM_Accept:\r
5098       AcceptEvent();\r
5099       break;\r
5100 \r
5101     case IDM_Decline:\r
5102       DeclineEvent();\r
5103       break;\r
5104 \r
5105     case IDM_Rematch:\r
5106       RematchEvent();\r
5107       break;\r
5108 \r
5109     case IDM_CallFlag:\r
5110       CallFlagEvent();\r
5111       break;\r
5112 \r
5113     case IDM_Draw:\r
5114       DrawEvent();\r
5115       break;\r
5116 \r
5117     case IDM_Adjourn:\r
5118       AdjournEvent();\r
5119       break;\r
5120 \r
5121     case IDM_Abort:\r
5122       AbortEvent();\r
5123       break;\r
5124 \r
5125     case IDM_Resign:\r
5126       ResignEvent();\r
5127       break;\r
5128 \r
5129     case IDM_StopObserving:\r
5130       StopObservingEvent();\r
5131       break;\r
5132 \r
5133     case IDM_StopExamining:\r
5134       StopExaminingEvent();\r
5135       break;\r
5136 \r
5137     case IDM_Upload:\r
5138       UploadGameEvent();\r
5139       break;\r
5140 \r
5141     case IDM_TypeInMove:\r
5142       TypeInEvent('\000');\r
5143       break;\r
5144 \r
5145     case IDM_TypeInName:\r
5146       PopUpNameDialog('\000');\r
5147       break;\r
5148 \r
5149     case IDM_Backward:\r
5150       BackwardEvent();\r
5151       SetFocus(hwndMain);\r
5152       break;\r
5153 \r
5154     JAWS_MENU_ITEMS\r
5155 \r
5156     case IDM_Forward:\r
5157       ForwardEvent();\r
5158       SetFocus(hwndMain);\r
5159       break;\r
5160 \r
5161     case IDM_ToStart:\r
5162       ToStartEvent();\r
5163       SetFocus(hwndMain);\r
5164       break;\r
5165 \r
5166     case IDM_ToEnd:\r
5167       ToEndEvent();\r
5168       SetFocus(hwndMain);\r
5169       break;\r
5170 \r
5171     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5172     case OPT_GameListPrev:\r
5173       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5174       break;\r
5175 \r
5176     case IDM_Revert:\r
5177       RevertEvent(FALSE);\r
5178       break;\r
5179 \r
5180     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5181       RevertEvent(TRUE);\r
5182       break;\r
5183 \r
5184     case IDM_TruncateGame:\r
5185       TruncateGameEvent();\r
5186       break;\r
5187 \r
5188     case IDM_MoveNow:\r
5189       MoveNowEvent();\r
5190       break;\r
5191 \r
5192     case IDM_RetractMove:\r
5193       RetractMoveEvent();\r
5194       break;\r
5195 \r
5196     case IDM_FlipView:\r
5197       flipView = !flipView;\r
5198       DrawPosition(FALSE, NULL);\r
5199       break;\r
5200 \r
5201     case IDM_FlipClock:\r
5202       flipClock = !flipClock;\r
5203       DisplayBothClocks();\r
5204       DisplayLogos();\r
5205       break;\r
5206 \r
5207     case IDM_MuteSounds:\r
5208       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5209       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5210                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5211       break;\r
5212 \r
5213     case IDM_GeneralOptions:\r
5214       GeneralOptionsPopup(hwnd);\r
5215       DrawPosition(TRUE, NULL);\r
5216       break;\r
5217 \r
5218     case IDM_BoardOptions:\r
5219       BoardOptionsPopup(hwnd);\r
5220       break;\r
5221 \r
5222     case IDM_ThemeOptions:\r
5223       ThemeOptionsPopup(hwnd);\r
5224       break;\r
5225 \r
5226     case IDM_EnginePlayOptions:\r
5227       EnginePlayOptionsPopup(hwnd);\r
5228       break;\r
5229 \r
5230     case IDM_Engine1Options:\r
5231       EngineOptionsPopup(hwnd, &first);\r
5232       break;\r
5233 \r
5234     case IDM_Engine2Options:\r
5235       savedHwnd = hwnd;\r
5236       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5237       EngineOptionsPopup(hwnd, &second);\r
5238       break;\r
5239 \r
5240     case IDM_OptionsUCI:\r
5241       UciOptionsPopup(hwnd);\r
5242       break;\r
5243 \r
5244     case IDM_Tourney:\r
5245       TourneyPopup(hwnd);\r
5246       break;\r
5247 \r
5248     case IDM_IcsOptions:\r
5249       IcsOptionsPopup(hwnd);\r
5250       break;\r
5251 \r
5252     case IDM_Fonts:\r
5253       FontsOptionsPopup(hwnd);\r
5254       break;\r
5255 \r
5256     case IDM_Sounds:\r
5257       SoundOptionsPopup(hwnd);\r
5258       break;\r
5259 \r
5260     case IDM_CommPort:\r
5261       CommPortOptionsPopup(hwnd);\r
5262       break;\r
5263 \r
5264     case IDM_LoadOptions:\r
5265       LoadOptionsPopup(hwnd);\r
5266       break;\r
5267 \r
5268     case IDM_SaveOptions:\r
5269       SaveOptionsPopup(hwnd);\r
5270       break;\r
5271 \r
5272     case IDM_TimeControl:\r
5273       TimeControlOptionsPopup(hwnd);\r
5274       break;\r
5275 \r
5276     case IDM_SaveSettings:\r
5277       SaveSettings(settingsFileName);\r
5278       break;\r
5279 \r
5280     case IDM_SaveSettingsOnExit:\r
5281       saveSettingsOnExit = !saveSettingsOnExit;\r
5282       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5283                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5284                                          MF_CHECKED : MF_UNCHECKED));\r
5285       break;\r
5286 \r
5287     case IDM_Hint:\r
5288       HintEvent();\r
5289       break;\r
5290 \r
5291     case IDM_Book:\r
5292       BookEvent();\r
5293       break;\r
5294 \r
5295     case IDM_AboutGame:\r
5296       AboutGameEvent();\r
5297       break;\r
5298 \r
5299     case IDM_Debug:\r
5300       appData.debugMode = !appData.debugMode;\r
5301       if (appData.debugMode) {\r
5302         char dir[MSG_SIZ];\r
5303         GetCurrentDirectory(MSG_SIZ, dir);\r
5304         SetCurrentDirectory(installDir);\r
5305         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5306         SetCurrentDirectory(dir);\r
5307         setbuf(debugFP, NULL);\r
5308       } else {\r
5309         fclose(debugFP);\r
5310         debugFP = NULL;\r
5311       }\r
5312       break;\r
5313 \r
5314     case IDM_HELPCONTENTS:\r
5315       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5316           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5317           MessageBox (GetFocus(),\r
5318                     _("Unable to activate help"),\r
5319                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5320       }\r
5321       break;\r
5322 \r
5323     case IDM_HELPSEARCH:\r
5324         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5325             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5326         MessageBox (GetFocus(),\r
5327                     _("Unable to activate help"),\r
5328                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5329       }\r
5330       break;\r
5331 \r
5332     case IDM_HELPHELP:\r
5333       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5334         MessageBox (GetFocus(),\r
5335                     _("Unable to activate help"),\r
5336                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5337       }\r
5338       break;\r
5339 \r
5340     case IDM_ABOUT:\r
5341       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5342       DialogBox(hInst, \r
5343         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5344         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5345       FreeProcInstance(lpProc);\r
5346       break;\r
5347 \r
5348     case IDM_DirectCommand1:\r
5349       AskQuestionEvent(_("Direct Command"),\r
5350                        _("Send to chess program:"), "", "1");\r
5351       break;\r
5352     case IDM_DirectCommand2:\r
5353       AskQuestionEvent(_("Direct Command"),\r
5354                        _("Send to second chess program:"), "", "2");\r
5355       break;\r
5356 \r
5357     case EP_WhitePawn:\r
5358       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5359       fromX = fromY = -1;\r
5360       break;\r
5361 \r
5362     case EP_WhiteKnight:\r
5363       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5364       fromX = fromY = -1;\r
5365       break;\r
5366 \r
5367     case EP_WhiteBishop:\r
5368       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5369       fromX = fromY = -1;\r
5370       break;\r
5371 \r
5372     case EP_WhiteRook:\r
5373       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5374       fromX = fromY = -1;\r
5375       break;\r
5376 \r
5377     case EP_WhiteQueen:\r
5378       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5379       fromX = fromY = -1;\r
5380       break;\r
5381 \r
5382     case EP_WhiteFerz:\r
5383       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5384       fromX = fromY = -1;\r
5385       break;\r
5386 \r
5387     case EP_WhiteWazir:\r
5388       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5389       fromX = fromY = -1;\r
5390       break;\r
5391 \r
5392     case EP_WhiteAlfil:\r
5393       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5394       fromX = fromY = -1;\r
5395       break;\r
5396 \r
5397     case EP_WhiteCannon:\r
5398       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5399       fromX = fromY = -1;\r
5400       break;\r
5401 \r
5402     case EP_WhiteCardinal:\r
5403       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5404       fromX = fromY = -1;\r
5405       break;\r
5406 \r
5407     case EP_WhiteMarshall:\r
5408       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5409       fromX = fromY = -1;\r
5410       break;\r
5411 \r
5412     case EP_WhiteKing:\r
5413       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5414       fromX = fromY = -1;\r
5415       break;\r
5416 \r
5417     case EP_BlackPawn:\r
5418       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5419       fromX = fromY = -1;\r
5420       break;\r
5421 \r
5422     case EP_BlackKnight:\r
5423       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5424       fromX = fromY = -1;\r
5425       break;\r
5426 \r
5427     case EP_BlackBishop:\r
5428       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5429       fromX = fromY = -1;\r
5430       break;\r
5431 \r
5432     case EP_BlackRook:\r
5433       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5434       fromX = fromY = -1;\r
5435       break;\r
5436 \r
5437     case EP_BlackQueen:\r
5438       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5439       fromX = fromY = -1;\r
5440       break;\r
5441 \r
5442     case EP_BlackFerz:\r
5443       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5444       fromX = fromY = -1;\r
5445       break;\r
5446 \r
5447     case EP_BlackWazir:\r
5448       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5449       fromX = fromY = -1;\r
5450       break;\r
5451 \r
5452     case EP_BlackAlfil:\r
5453       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5454       fromX = fromY = -1;\r
5455       break;\r
5456 \r
5457     case EP_BlackCannon:\r
5458       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5459       fromX = fromY = -1;\r
5460       break;\r
5461 \r
5462     case EP_BlackCardinal:\r
5463       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5464       fromX = fromY = -1;\r
5465       break;\r
5466 \r
5467     case EP_BlackMarshall:\r
5468       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5469       fromX = fromY = -1;\r
5470       break;\r
5471 \r
5472     case EP_BlackKing:\r
5473       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5474       fromX = fromY = -1;\r
5475       break;\r
5476 \r
5477     case EP_EmptySquare:\r
5478       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5479       fromX = fromY = -1;\r
5480       break;\r
5481 \r
5482     case EP_ClearBoard:\r
5483       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5484       fromX = fromY = -1;\r
5485       break;\r
5486 \r
5487     case EP_White:\r
5488       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5489       fromX = fromY = -1;\r
5490       break;\r
5491 \r
5492     case EP_Black:\r
5493       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5494       fromX = fromY = -1;\r
5495       break;\r
5496 \r
5497     case EP_Promote:\r
5498       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5499       fromX = fromY = -1;\r
5500       break;\r
5501 \r
5502     case EP_Demote:\r
5503       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5504       fromX = fromY = -1;\r
5505       break;\r
5506 \r
5507     case DP_Pawn:\r
5508       DropMenuEvent(WhitePawn, fromX, fromY);\r
5509       fromX = fromY = -1;\r
5510       break;\r
5511 \r
5512     case DP_Knight:\r
5513       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5514       fromX = fromY = -1;\r
5515       break;\r
5516 \r
5517     case DP_Bishop:\r
5518       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5519       fromX = fromY = -1;\r
5520       break;\r
5521 \r
5522     case DP_Rook:\r
5523       DropMenuEvent(WhiteRook, fromX, fromY);\r
5524       fromX = fromY = -1;\r
5525       break;\r
5526 \r
5527     case DP_Queen:\r
5528       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5529       fromX = fromY = -1;\r
5530       break;\r
5531 \r
5532     case IDM_English:\r
5533       barbaric = 0; appData.language = "";\r
5534       TranslateMenus(0);\r
5535       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5536       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5537       lastChecked = wmId;\r
5538       break;\r
5539 \r
5540     default:\r
5541       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5542           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5543       else\r
5544       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5545           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5546           TranslateMenus(0);\r
5547           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5548           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5549           lastChecked = wmId;\r
5550           break;\r
5551       }\r
5552       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5553     }\r
5554     break;\r
5555 \r
5556   case WM_TIMER:\r
5557     switch (wParam) {\r
5558     case CLOCK_TIMER_ID:\r
5559       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5560       clockTimerEvent = 0;\r
5561       DecrementClocks(); /* call into back end */\r
5562       break;\r
5563     case LOAD_GAME_TIMER_ID:\r
5564       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5565       loadGameTimerEvent = 0;\r
5566       AutoPlayGameLoop(); /* call into back end */\r
5567       break;\r
5568     case ANALYSIS_TIMER_ID:\r
5569       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5570                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5571         AnalysisPeriodicEvent(0);\r
5572       } else {\r
5573         KillTimer(hwnd, analysisTimerEvent);\r
5574         analysisTimerEvent = 0;\r
5575       }\r
5576       break;\r
5577     case DELAYED_TIMER_ID:\r
5578       KillTimer(hwnd, delayedTimerEvent);\r
5579       delayedTimerEvent = 0;\r
5580       delayedTimerCallback();\r
5581       break;\r
5582     }\r
5583     break;\r
5584 \r
5585   case WM_USER_Input:\r
5586     InputEvent(hwnd, message, wParam, lParam);\r
5587     break;\r
5588 \r
5589   /* [AS] Also move "attached" child windows */\r
5590   case WM_WINDOWPOSCHANGING:\r
5591 \r
5592     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5593         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5594 \r
5595         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5596             /* Window is moving */\r
5597             RECT rcMain;\r
5598 \r
5599 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5600             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5601             rcMain.right  = wpMain.x + wpMain.width;\r
5602             rcMain.top    = wpMain.y;\r
5603             rcMain.bottom = wpMain.y + wpMain.height;\r
5604             \r
5605             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5606             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5607             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5608             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5609             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5610             wpMain.x = lpwp->x;\r
5611             wpMain.y = lpwp->y;\r
5612         }\r
5613     }\r
5614     break;\r
5615 \r
5616   /* [AS] Snapping */\r
5617   case WM_ENTERSIZEMOVE:\r
5618     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5619     if (hwnd == hwndMain) {\r
5620       doingSizing = TRUE;\r
5621       lastSizing = 0;\r
5622     }\r
5623     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5624     break;\r
5625 \r
5626   case WM_SIZING:\r
5627     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5628     if (hwnd == hwndMain) {\r
5629       lastSizing = wParam;\r
5630     }\r
5631     break;\r
5632 \r
5633   case WM_MOVING:\r
5634     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5635       return OnMoving( &sd, hwnd, wParam, lParam );\r
5636 \r
5637   case WM_EXITSIZEMOVE:\r
5638     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5639     if (hwnd == hwndMain) {\r
5640       RECT client;\r
5641       doingSizing = FALSE;\r
5642       InvalidateRect(hwnd, &boardRect, FALSE);\r
5643       GetClientRect(hwnd, &client);\r
5644       ResizeBoard(client.right, client.bottom, lastSizing);\r
5645       lastSizing = 0;\r
5646       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5647     }\r
5648     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5649     break;\r
5650 \r
5651   case WM_DESTROY: /* message: window being destroyed */\r
5652     PostQuitMessage(0);\r
5653     break;\r
5654 \r
5655   case WM_CLOSE:\r
5656     if (hwnd == hwndMain) {\r
5657       ExitEvent(0);\r
5658     }\r
5659     break;\r
5660 \r
5661   default:      /* Passes it on if unprocessed */\r
5662     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5663   }\r
5664   return 0;\r
5665 }\r
5666 \r
5667 /*---------------------------------------------------------------------------*\\r
5668  *\r
5669  * Misc utility routines\r
5670  *\r
5671 \*---------------------------------------------------------------------------*/\r
5672 \r
5673 /*\r
5674  * Decent random number generator, at least not as bad as Windows\r
5675  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5676  */\r
5677 unsigned int randstate;\r
5678 \r
5679 int\r
5680 myrandom(void)\r
5681 {\r
5682   randstate = randstate * 1664525 + 1013904223;\r
5683   return (int) randstate & 0x7fffffff;\r
5684 }\r
5685 \r
5686 void\r
5687 mysrandom(unsigned int seed)\r
5688 {\r
5689   randstate = seed;\r
5690 }\r
5691 \r
5692 \r
5693 /* \r
5694  * returns TRUE if user selects a different color, FALSE otherwise \r
5695  */\r
5696 \r
5697 BOOL\r
5698 ChangeColor(HWND hwnd, COLORREF *which)\r
5699 {\r
5700   static BOOL firstTime = TRUE;\r
5701   static DWORD customColors[16];\r
5702   CHOOSECOLOR cc;\r
5703   COLORREF newcolor;\r
5704   int i;\r
5705   ColorClass ccl;\r
5706 \r
5707   if (firstTime) {\r
5708     /* Make initial colors in use available as custom colors */\r
5709     /* Should we put the compiled-in defaults here instead? */\r
5710     i = 0;\r
5711     customColors[i++] = lightSquareColor & 0xffffff;\r
5712     customColors[i++] = darkSquareColor & 0xffffff;\r
5713     customColors[i++] = whitePieceColor & 0xffffff;\r
5714     customColors[i++] = blackPieceColor & 0xffffff;\r
5715     customColors[i++] = highlightSquareColor & 0xffffff;\r
5716     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5717 \r
5718     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5719       customColors[i++] = textAttribs[ccl].color;\r
5720     }\r
5721     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5722     firstTime = FALSE;\r
5723   }\r
5724 \r
5725   cc.lStructSize = sizeof(cc);\r
5726   cc.hwndOwner = hwnd;\r
5727   cc.hInstance = NULL;\r
5728   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5729   cc.lpCustColors = (LPDWORD) customColors;\r
5730   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5731 \r
5732   if (!ChooseColor(&cc)) return FALSE;\r
5733 \r
5734   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5735   if (newcolor == *which) return FALSE;\r
5736   *which = newcolor;\r
5737   return TRUE;\r
5738 \r
5739   /*\r
5740   InitDrawingColors();\r
5741   InvalidateRect(hwnd, &boardRect, FALSE);\r
5742   */\r
5743 }\r
5744 \r
5745 BOOLEAN\r
5746 MyLoadSound(MySound *ms)\r
5747 {\r
5748   BOOL ok = FALSE;\r
5749   struct stat st;\r
5750   FILE *f;\r
5751 \r
5752   if (ms->data && ms->flag) free(ms->data);\r
5753   ms->data = NULL;\r
5754 \r
5755   switch (ms->name[0]) {\r
5756   case NULLCHAR:\r
5757     /* Silence */\r
5758     ok = TRUE;\r
5759     break;\r
5760   case '$':\r
5761     /* System sound from Control Panel.  Don't preload here. */\r
5762     ok = TRUE;\r
5763     break;\r
5764   case '!':\r
5765     if (ms->name[1] == NULLCHAR) {\r
5766       /* "!" alone = silence */\r
5767       ok = TRUE;\r
5768     } else {\r
5769       /* Builtin wave resource.  Error if not found. */\r
5770       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5771       if (h == NULL) break;\r
5772       ms->data = (void *)LoadResource(hInst, h);\r
5773       ms->flag = 0; // not maloced, so cannot be freed!\r
5774       if (h == NULL) break;\r
5775       ok = TRUE;\r
5776     }\r
5777     break;\r
5778   default:\r
5779     /* .wav file.  Error if not found. */\r
5780     f = fopen(ms->name, "rb");\r
5781     if (f == NULL) break;\r
5782     if (fstat(fileno(f), &st) < 0) break;\r
5783     ms->data = malloc(st.st_size);\r
5784     ms->flag = 1;\r
5785     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5786     fclose(f);\r
5787     ok = TRUE;\r
5788     break;\r
5789   }\r
5790   if (!ok) {\r
5791     char buf[MSG_SIZ];\r
5792       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5793     DisplayError(buf, GetLastError());\r
5794   }\r
5795   return ok;\r
5796 }\r
5797 \r
5798 BOOLEAN\r
5799 MyPlaySound(MySound *ms)\r
5800 {\r
5801   BOOLEAN ok = FALSE;\r
5802 \r
5803   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5804   switch (ms->name[0]) {\r
5805   case NULLCHAR:\r
5806         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5807     /* Silence */\r
5808     ok = TRUE;\r
5809     break;\r
5810   case '$':\r
5811     /* System sound from Control Panel (deprecated feature).\r
5812        "$" alone or an unset sound name gets default beep (still in use). */\r
5813     if (ms->name[1]) {\r
5814       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5815     }\r
5816     if (!ok) ok = MessageBeep(MB_OK);\r
5817     break; \r
5818   case '!':\r
5819     /* Builtin wave resource, or "!" alone for silence */\r
5820     if (ms->name[1]) {\r
5821       if (ms->data == NULL) return FALSE;\r
5822       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5823     } else {\r
5824       ok = TRUE;\r
5825     }\r
5826     break;\r
5827   default:\r
5828     /* .wav file.  Error if not found. */\r
5829     if (ms->data == NULL) return FALSE;\r
5830     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5831     break;\r
5832   }\r
5833   /* Don't print an error: this can happen innocently if the sound driver\r
5834      is busy; for instance, if another instance of WinBoard is playing\r
5835      a sound at about the same time. */\r
5836   return ok;\r
5837 }\r
5838 \r
5839 \r
5840 LRESULT CALLBACK\r
5841 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5842 {\r
5843   BOOL ok;\r
5844   OPENFILENAME *ofn;\r
5845   static UINT *number; /* gross that this is static */\r
5846 \r
5847   switch (message) {\r
5848   case WM_INITDIALOG: /* message: initialize dialog box */\r
5849     /* Center the dialog over the application window */\r
5850     ofn = (OPENFILENAME *) lParam;\r
5851     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5852       number = (UINT *) ofn->lCustData;\r
5853       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5854     } else {\r
5855       number = NULL;\r
5856     }\r
5857     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5858     Translate(hDlg, 1536);\r
5859     return FALSE;  /* Allow for further processing */\r
5860 \r
5861   case WM_COMMAND:\r
5862     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5863       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5864     }\r
5865     return FALSE;  /* Allow for further processing */\r
5866   }\r
5867   return FALSE;\r
5868 }\r
5869 \r
5870 UINT APIENTRY\r
5871 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5872 {\r
5873   static UINT *number;\r
5874   OPENFILENAME *ofname;\r
5875   OFNOTIFY *ofnot;\r
5876   switch (uiMsg) {\r
5877   case WM_INITDIALOG:\r
5878     Translate(hdlg, DLG_IndexNumber);\r
5879     ofname = (OPENFILENAME *)lParam;\r
5880     number = (UINT *)(ofname->lCustData);\r
5881     break;\r
5882   case WM_NOTIFY:\r
5883     ofnot = (OFNOTIFY *)lParam;\r
5884     if (ofnot->hdr.code == CDN_FILEOK) {\r
5885       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5886     }\r
5887     break;\r
5888   }\r
5889   return 0;\r
5890 }\r
5891 \r
5892 \r
5893 FILE *\r
5894 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5895                char *nameFilt, char *dlgTitle, UINT *number,\r
5896                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5897 {\r
5898   OPENFILENAME openFileName;\r
5899   char buf1[MSG_SIZ];\r
5900   FILE *f;\r
5901 \r
5902   if (fileName == NULL) fileName = buf1;\r
5903   if (defName == NULL) {\r
5904     safeStrCpy(fileName, "*.", 3 );\r
5905     strcat(fileName, defExt);\r
5906   } else {\r
5907     safeStrCpy(fileName, defName, MSG_SIZ );\r
5908   }\r
5909     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5910   if (number) *number = 0;\r
5911 \r
5912   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5913   openFileName.hwndOwner         = hwnd;\r
5914   openFileName.hInstance         = (HANDLE) hInst;\r
5915   openFileName.lpstrFilter       = nameFilt;\r
5916   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5917   openFileName.nMaxCustFilter    = 0L;\r
5918   openFileName.nFilterIndex      = 1L;\r
5919   openFileName.lpstrFile         = fileName;\r
5920   openFileName.nMaxFile          = MSG_SIZ;\r
5921   openFileName.lpstrFileTitle    = fileTitle;\r
5922   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5923   openFileName.lpstrInitialDir   = NULL;\r
5924   openFileName.lpstrTitle        = dlgTitle;\r
5925   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5926     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5927     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5928     | (oldDialog ? 0 : OFN_EXPLORER);\r
5929   openFileName.nFileOffset       = 0;\r
5930   openFileName.nFileExtension    = 0;\r
5931   openFileName.lpstrDefExt       = defExt;\r
5932   openFileName.lCustData         = (LONG) number;\r
5933   openFileName.lpfnHook          = oldDialog ?\r
5934     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5935   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5936 \r
5937   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5938                         GetOpenFileName(&openFileName)) {\r
5939     /* open the file */\r
5940     f = fopen(openFileName.lpstrFile, write);\r
5941     if (f == NULL) {\r
5942       MessageBox(hwnd, _("File open failed"), NULL,\r
5943                  MB_OK|MB_ICONEXCLAMATION);\r
5944       return NULL;\r
5945     }\r
5946   } else {\r
5947     int err = CommDlgExtendedError();\r
5948     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5949     return FALSE;\r
5950   }\r
5951   return f;\r
5952 }\r
5953 \r
5954 \r
5955 \r
5956 VOID APIENTRY\r
5957 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5958 {\r
5959   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5960 \r
5961   /*\r
5962    * Get the first pop-up menu in the menu template. This is the\r
5963    * menu that TrackPopupMenu displays.\r
5964    */\r
5965   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5966   TranslateOneMenu(10, hmenuTrackPopup);\r
5967 \r
5968   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5969 \r
5970   /*\r
5971    * TrackPopup uses screen coordinates, so convert the\r
5972    * coordinates of the mouse click to screen coordinates.\r
5973    */\r
5974   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5975 \r
5976   /* Draw and track the floating pop-up menu. */\r
5977   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5978                  pt.x, pt.y, 0, hwnd, NULL);\r
5979 \r
5980   /* Destroy the menu.*/\r
5981   DestroyMenu(hmenu);\r
5982 }\r
5983    \r
5984 typedef struct {\r
5985   HWND hDlg, hText;\r
5986   int sizeX, sizeY, newSizeX, newSizeY;\r
5987   HDWP hdwp;\r
5988 } ResizeEditPlusButtonsClosure;\r
5989 \r
5990 BOOL CALLBACK\r
5991 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5992 {\r
5993   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5994   RECT rect;\r
5995   POINT pt;\r
5996 \r
5997   if (hChild == cl->hText) return TRUE;\r
5998   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5999   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6000   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6001   ScreenToClient(cl->hDlg, &pt);\r
6002   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6003     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6004   return TRUE;\r
6005 }\r
6006 \r
6007 /* Resize a dialog that has a (rich) edit field filling most of\r
6008    the top, with a row of buttons below */\r
6009 VOID\r
6010 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6011 {\r
6012   RECT rectText;\r
6013   int newTextHeight, newTextWidth;\r
6014   ResizeEditPlusButtonsClosure cl;\r
6015   \r
6016   /*if (IsIconic(hDlg)) return;*/\r
6017   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6018   \r
6019   cl.hdwp = BeginDeferWindowPos(8);\r
6020 \r
6021   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6022   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6023   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6024   if (newTextHeight < 0) {\r
6025     newSizeY += -newTextHeight;\r
6026     newTextHeight = 0;\r
6027   }\r
6028   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6029     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6030 \r
6031   cl.hDlg = hDlg;\r
6032   cl.hText = hText;\r
6033   cl.sizeX = sizeX;\r
6034   cl.sizeY = sizeY;\r
6035   cl.newSizeX = newSizeX;\r
6036   cl.newSizeY = newSizeY;\r
6037   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6038 \r
6039   EndDeferWindowPos(cl.hdwp);\r
6040 }\r
6041 \r
6042 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6043 {\r
6044     RECT    rChild, rParent;\r
6045     int     wChild, hChild, wParent, hParent;\r
6046     int     wScreen, hScreen, xNew, yNew;\r
6047     HDC     hdc;\r
6048 \r
6049     /* Get the Height and Width of the child window */\r
6050     GetWindowRect (hwndChild, &rChild);\r
6051     wChild = rChild.right - rChild.left;\r
6052     hChild = rChild.bottom - rChild.top;\r
6053 \r
6054     /* Get the Height and Width of the parent window */\r
6055     GetWindowRect (hwndParent, &rParent);\r
6056     wParent = rParent.right - rParent.left;\r
6057     hParent = rParent.bottom - rParent.top;\r
6058 \r
6059     /* Get the display limits */\r
6060     hdc = GetDC (hwndChild);\r
6061     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6062     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6063     ReleaseDC(hwndChild, hdc);\r
6064 \r
6065     /* Calculate new X position, then adjust for screen */\r
6066     xNew = rParent.left + ((wParent - wChild) /2);\r
6067     if (xNew < 0) {\r
6068         xNew = 0;\r
6069     } else if ((xNew+wChild) > wScreen) {\r
6070         xNew = wScreen - wChild;\r
6071     }\r
6072 \r
6073     /* Calculate new Y position, then adjust for screen */\r
6074     if( mode == 0 ) {\r
6075         yNew = rParent.top  + ((hParent - hChild) /2);\r
6076     }\r
6077     else {\r
6078         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6079     }\r
6080 \r
6081     if (yNew < 0) {\r
6082         yNew = 0;\r
6083     } else if ((yNew+hChild) > hScreen) {\r
6084         yNew = hScreen - hChild;\r
6085     }\r
6086 \r
6087     /* Set it, and return */\r
6088     return SetWindowPos (hwndChild, NULL,\r
6089                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6090 }\r
6091 \r
6092 /* Center one window over another */\r
6093 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6094 {\r
6095     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6096 }\r
6097 \r
6098 /*---------------------------------------------------------------------------*\\r
6099  *\r
6100  * Startup Dialog functions\r
6101  *\r
6102 \*---------------------------------------------------------------------------*/\r
6103 void\r
6104 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6105 {\r
6106   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6107 \r
6108   while (*cd != NULL) {\r
6109     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6110     cd++;\r
6111   }\r
6112 }\r
6113 \r
6114 void\r
6115 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6116 {\r
6117   char buf1[MAX_ARG_LEN];\r
6118   int len;\r
6119 \r
6120   if (str[0] == '@') {\r
6121     FILE* f = fopen(str + 1, "r");\r
6122     if (f == NULL) {\r
6123       DisplayFatalError(str + 1, errno, 2);\r
6124       return;\r
6125     }\r
6126     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6127     fclose(f);\r
6128     buf1[len] = NULLCHAR;\r
6129     str = buf1;\r
6130   }\r
6131 \r
6132   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6133 \r
6134   for (;;) {\r
6135     char buf[MSG_SIZ];\r
6136     char *end = strchr(str, '\n');\r
6137     if (end == NULL) return;\r
6138     memcpy(buf, str, end - str);\r
6139     buf[end - str] = NULLCHAR;\r
6140     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6141     str = end + 1;\r
6142   }\r
6143 }\r
6144 \r
6145 void\r
6146 SetStartupDialogEnables(HWND hDlg)\r
6147 {\r
6148   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6149     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6150     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6151   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6152     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6153   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6154     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6155   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6156     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6157   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6158     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6159     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6160     IsDlgButtonChecked(hDlg, OPT_View));\r
6161 }\r
6162 \r
6163 char *\r
6164 QuoteForFilename(char *filename)\r
6165 {\r
6166   int dquote, space;\r
6167   dquote = strchr(filename, '"') != NULL;\r
6168   space = strchr(filename, ' ') != NULL;\r
6169   if (dquote || space) {\r
6170     if (dquote) {\r
6171       return "'";\r
6172     } else {\r
6173       return "\"";\r
6174     }\r
6175   } else {\r
6176     return "";\r
6177   }\r
6178 }\r
6179 \r
6180 VOID\r
6181 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6182 {\r
6183   char buf[MSG_SIZ];\r
6184   char *q;\r
6185 \r
6186   InitComboStringsFromOption(hwndCombo, nthnames);\r
6187   q = QuoteForFilename(nthcp);\r
6188     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6189   if (*nthdir != NULLCHAR) {\r
6190     q = QuoteForFilename(nthdir);\r
6191       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6192   }\r
6193   if (*nthcp == NULLCHAR) {\r
6194     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6195   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6196     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6197     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6198   }\r
6199 }\r
6200 \r
6201 LRESULT CALLBACK\r
6202 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6203 {\r
6204   char buf[MSG_SIZ];\r
6205   HANDLE hwndCombo;\r
6206   char *p;\r
6207 \r
6208   switch (message) {\r
6209   case WM_INITDIALOG:\r
6210     /* Center the dialog */\r
6211     CenterWindow (hDlg, GetDesktopWindow());\r
6212     Translate(hDlg, DLG_Startup);\r
6213     /* Initialize the dialog items */\r
6214     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6215                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6216                   firstChessProgramNames);\r
6217     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6218                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6219                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6220     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6221     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6222       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6223     if (*appData.icsHelper != NULLCHAR) {\r
6224       char *q = QuoteForFilename(appData.icsHelper);\r
6225       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6226     }\r
6227     if (*appData.icsHost == NULLCHAR) {\r
6228       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6229       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6230     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6231       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6232       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6233     }\r
6234 \r
6235     if (appData.icsActive) {\r
6236       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6237     }\r
6238     else if (appData.noChessProgram) {\r
6239       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6240     }\r
6241     else {\r
6242       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6243     }\r
6244 \r
6245     SetStartupDialogEnables(hDlg);\r
6246     return TRUE;\r
6247 \r
6248   case WM_COMMAND:\r
6249     switch (LOWORD(wParam)) {\r
6250     case IDOK:\r
6251       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6252         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6253         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6254         p = buf;\r
6255         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6256         ParseArgs(StringGet, &p);\r
6257         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6258         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6259         p = buf;\r
6260         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6261         ParseArgs(StringGet, &p);\r
6262         SwapEngines(singleList); // ... and then make it 'second'\r
6263         appData.noChessProgram = FALSE;\r
6264         appData.icsActive = FALSE;\r
6265       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6266         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6267         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6268         p = buf;\r
6269         ParseArgs(StringGet, &p);\r
6270         if (appData.zippyPlay) {\r
6271           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6272           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6273           p = buf;\r
6274           ParseArgs(StringGet, &p);\r
6275         }\r
6276       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6277         appData.noChessProgram = TRUE;\r
6278         appData.icsActive = FALSE;\r
6279       } else {\r
6280         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6281                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6282         return TRUE;\r
6283       }\r
6284       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6285         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6286         p = buf;\r
6287         ParseArgs(StringGet, &p);\r
6288       }\r
6289       EndDialog(hDlg, TRUE);\r
6290       return TRUE;\r
6291 \r
6292     case IDCANCEL:\r
6293       ExitEvent(0);\r
6294       return TRUE;\r
6295 \r
6296     case IDM_HELPCONTENTS:\r
6297       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6298         MessageBox (GetFocus(),\r
6299                     _("Unable to activate help"),\r
6300                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6301       }\r
6302       break;\r
6303 \r
6304     default:\r
6305       SetStartupDialogEnables(hDlg);\r
6306       break;\r
6307     }\r
6308     break;\r
6309   }\r
6310   return FALSE;\r
6311 }\r
6312 \r
6313 /*---------------------------------------------------------------------------*\\r
6314  *\r
6315  * About box dialog functions\r
6316  *\r
6317 \*---------------------------------------------------------------------------*/\r
6318 \r
6319 /* Process messages for "About" dialog box */\r
6320 LRESULT CALLBACK\r
6321 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6322 {\r
6323   switch (message) {\r
6324   case WM_INITDIALOG: /* message: initialize dialog box */\r
6325     /* Center the dialog over the application window */\r
6326     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6327     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6328     Translate(hDlg, ABOUTBOX);\r
6329     JAWS_COPYRIGHT\r
6330     return (TRUE);\r
6331 \r
6332   case WM_COMMAND: /* message: received a command */\r
6333     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6334         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6335       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6336       return (TRUE);\r
6337     }\r
6338     break;\r
6339   }\r
6340   return (FALSE);\r
6341 }\r
6342 \r
6343 /*---------------------------------------------------------------------------*\\r
6344  *\r
6345  * Comment Dialog functions\r
6346  *\r
6347 \*---------------------------------------------------------------------------*/\r
6348 \r
6349 LRESULT CALLBACK\r
6350 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6351 {\r
6352   static HANDLE hwndText = NULL;\r
6353   int len, newSizeX, newSizeY, flags;\r
6354   static int sizeX, sizeY;\r
6355   char *str;\r
6356   RECT rect;\r
6357   MINMAXINFO *mmi;\r
6358 \r
6359   switch (message) {\r
6360   case WM_INITDIALOG: /* message: initialize dialog box */\r
6361     /* Initialize the dialog items */\r
6362     Translate(hDlg, DLG_EditComment);\r
6363     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6364     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6365     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6366     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6367     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6368     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6369     SetWindowText(hDlg, commentTitle);\r
6370     if (editComment) {\r
6371       SetFocus(hwndText);\r
6372     } else {\r
6373       SetFocus(GetDlgItem(hDlg, IDOK));\r
6374     }\r
6375     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6376                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6377                 MAKELPARAM(FALSE, 0));\r
6378     /* Size and position the dialog */\r
6379     if (!commentDialog) {\r
6380       commentDialog = hDlg;\r
6381       flags = SWP_NOZORDER;\r
6382       GetClientRect(hDlg, &rect);\r
6383       sizeX = rect.right;\r
6384       sizeY = rect.bottom;\r
6385       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6386           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6387         WINDOWPLACEMENT wp;\r
6388         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6389         wp.length = sizeof(WINDOWPLACEMENT);\r
6390         wp.flags = 0;\r
6391         wp.showCmd = SW_SHOW;\r
6392         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6393         wp.rcNormalPosition.left = wpComment.x;\r
6394         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6395         wp.rcNormalPosition.top = wpComment.y;\r
6396         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6397         SetWindowPlacement(hDlg, &wp);\r
6398 \r
6399         GetClientRect(hDlg, &rect);\r
6400         newSizeX = rect.right;\r
6401         newSizeY = rect.bottom;\r
6402         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6403                               newSizeX, newSizeY);\r
6404         sizeX = newSizeX;\r
6405         sizeY = newSizeY;\r
6406       }\r
6407     }\r
6408     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6409     return FALSE;\r
6410 \r
6411   case WM_COMMAND: /* message: received a command */\r
6412     switch (LOWORD(wParam)) {\r
6413     case IDOK:\r
6414       if (editComment) {\r
6415         char *p, *q;\r
6416         /* Read changed options from the dialog box */\r
6417         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6418         len = GetWindowTextLength(hwndText);\r
6419         str = (char *) malloc(len + 1);\r
6420         GetWindowText(hwndText, str, len + 1);\r
6421         p = q = str;\r
6422         while (*q) {\r
6423           if (*q == '\r')\r
6424             q++;\r
6425           else\r
6426             *p++ = *q++;\r
6427         }\r
6428         *p = NULLCHAR;\r
6429         ReplaceComment(commentIndex, str);\r
6430         free(str);\r
6431       }\r
6432       CommentPopDown();\r
6433       return TRUE;\r
6434 \r
6435     case IDCANCEL:\r
6436     case OPT_CancelComment:\r
6437       CommentPopDown();\r
6438       return TRUE;\r
6439 \r
6440     case OPT_ClearComment:\r
6441       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6442       break;\r
6443 \r
6444     case OPT_EditComment:\r
6445       EditCommentEvent();\r
6446       return TRUE;\r
6447 \r
6448     default:\r
6449       break;\r
6450     }\r
6451     break;\r
6452 \r
6453   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6454         if( wParam == OPT_CommentText ) {\r
6455             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6456 \r
6457             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6458                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6459                 POINTL pt;\r
6460                 LRESULT index;\r
6461 \r
6462                 pt.x = LOWORD( lpMF->lParam );\r
6463                 pt.y = HIWORD( lpMF->lParam );\r
6464 \r
6465                 if(lpMF->msg == WM_CHAR) {\r
6466                         CHARRANGE sel;\r
6467                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6468                         index = sel.cpMin;\r
6469                 } else\r
6470                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6471 \r
6472                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6473                 len = GetWindowTextLength(hwndText);\r
6474                 str = (char *) malloc(len + 1);\r
6475                 GetWindowText(hwndText, str, len + 1);\r
6476                 ReplaceComment(commentIndex, str);\r
6477                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6478                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6479                 free(str);\r
6480 \r
6481                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6482                 lpMF->msg = WM_USER;\r
6483 \r
6484                 return TRUE;\r
6485             }\r
6486         }\r
6487         break;\r
6488 \r
6489   case WM_SIZE:\r
6490     newSizeX = LOWORD(lParam);\r
6491     newSizeY = HIWORD(lParam);\r
6492     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6493     sizeX = newSizeX;\r
6494     sizeY = newSizeY;\r
6495     break;\r
6496 \r
6497   case WM_GETMINMAXINFO:\r
6498     /* Prevent resizing window too small */\r
6499     mmi = (MINMAXINFO *) lParam;\r
6500     mmi->ptMinTrackSize.x = 100;\r
6501     mmi->ptMinTrackSize.y = 100;\r
6502     break;\r
6503   }\r
6504   return FALSE;\r
6505 }\r
6506 \r
6507 VOID\r
6508 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6509 {\r
6510   FARPROC lpProc;\r
6511   char *p, *q;\r
6512 \r
6513   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6514 \r
6515   if (str == NULL) str = "";\r
6516   p = (char *) malloc(2 * strlen(str) + 2);\r
6517   q = p;\r
6518   while (*str) {\r
6519     if (*str == '\n') *q++ = '\r';\r
6520     *q++ = *str++;\r
6521   }\r
6522   *q = NULLCHAR;\r
6523   if (commentText != NULL) free(commentText);\r
6524 \r
6525   commentIndex = index;\r
6526   commentTitle = title;\r
6527   commentText = p;\r
6528   editComment = edit;\r
6529 \r
6530   if (commentDialog) {\r
6531     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6532     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6533   } else {\r
6534     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6535     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6536                  hwndMain, (DLGPROC)lpProc);\r
6537     FreeProcInstance(lpProc);\r
6538   }\r
6539   commentUp = TRUE;\r
6540 }\r
6541 \r
6542 \r
6543 /*---------------------------------------------------------------------------*\\r
6544  *\r
6545  * Type-in move dialog functions\r
6546  * \r
6547 \*---------------------------------------------------------------------------*/\r
6548 \r
6549 LRESULT CALLBACK\r
6550 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6551 {\r
6552   char move[MSG_SIZ];\r
6553   HWND hInput;\r
6554 \r
6555   switch (message) {\r
6556   case WM_INITDIALOG:\r
6557     move[0] = (char) lParam;\r
6558     move[1] = NULLCHAR;\r
6559     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6560     Translate(hDlg, DLG_TypeInMove);\r
6561     hInput = GetDlgItem(hDlg, OPT_Move);\r
6562     SetWindowText(hInput, move);\r
6563     SetFocus(hInput);\r
6564     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6565     return FALSE;\r
6566 \r
6567   case WM_COMMAND:\r
6568     switch (LOWORD(wParam)) {\r
6569     case IDOK:\r
6570 \r
6571       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6572       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6573       TypeInDoneEvent(move);\r
6574       EndDialog(hDlg, TRUE);\r
6575       return TRUE;\r
6576     case IDCANCEL:\r
6577       EndDialog(hDlg, FALSE);\r
6578       return TRUE;\r
6579     default:\r
6580       break;\r
6581     }\r
6582     break;\r
6583   }\r
6584   return FALSE;\r
6585 }\r
6586 \r
6587 VOID\r
6588 PopUpMoveDialog(char firstchar)\r
6589 {\r
6590     FARPROC lpProc;\r
6591 \r
6592       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6593       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6594         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6595       FreeProcInstance(lpProc);\r
6596 }\r
6597 \r
6598 /*---------------------------------------------------------------------------*\\r
6599  *\r
6600  * Type-in name dialog functions\r
6601  * \r
6602 \*---------------------------------------------------------------------------*/\r
6603 \r
6604 LRESULT CALLBACK\r
6605 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6606 {\r
6607   char move[MSG_SIZ];\r
6608   HWND hInput;\r
6609 \r
6610   switch (message) {\r
6611   case WM_INITDIALOG:\r
6612     move[0] = (char) lParam;\r
6613     move[1] = NULLCHAR;\r
6614     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6615     Translate(hDlg, DLG_TypeInName);\r
6616     hInput = GetDlgItem(hDlg, OPT_Name);\r
6617     SetWindowText(hInput, move);\r
6618     SetFocus(hInput);\r
6619     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6620     return FALSE;\r
6621 \r
6622   case WM_COMMAND:\r
6623     switch (LOWORD(wParam)) {\r
6624     case IDOK:\r
6625       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6626       appData.userName = strdup(move);\r
6627       SetUserLogo();\r
6628       SetGameInfo();\r
6629       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6630         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6631         DisplayTitle(move);\r
6632       }\r
6633 \r
6634 \r
6635       EndDialog(hDlg, TRUE);\r
6636       return TRUE;\r
6637     case IDCANCEL:\r
6638       EndDialog(hDlg, FALSE);\r
6639       return TRUE;\r
6640     default:\r
6641       break;\r
6642     }\r
6643     break;\r
6644   }\r
6645   return FALSE;\r
6646 }\r
6647 \r
6648 VOID\r
6649 PopUpNameDialog(char firstchar)\r
6650 {\r
6651     FARPROC lpProc;\r
6652     \r
6653       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6654       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6655         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6656       FreeProcInstance(lpProc);\r
6657 }\r
6658 \r
6659 /*---------------------------------------------------------------------------*\\r
6660  *\r
6661  *  Error dialogs\r
6662  * \r
6663 \*---------------------------------------------------------------------------*/\r
6664 \r
6665 /* Nonmodal error box */\r
6666 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6667                              WPARAM wParam, LPARAM lParam);\r
6668 \r
6669 VOID\r
6670 ErrorPopUp(char *title, char *content)\r
6671 {\r
6672   FARPROC lpProc;\r
6673   char *p, *q;\r
6674   BOOLEAN modal = hwndMain == NULL;\r
6675 \r
6676   p = content;\r
6677   q = errorMessage;\r
6678   while (*p) {\r
6679     if (*p == '\n') {\r
6680       if (modal) {\r
6681         *q++ = ' ';\r
6682         p++;\r
6683       } else {\r
6684         *q++ = '\r';\r
6685         *q++ = *p++;\r
6686       }\r
6687     } else {\r
6688       *q++ = *p++;\r
6689     }\r
6690   }\r
6691   *q = NULLCHAR;\r
6692   strncpy(errorTitle, title, sizeof(errorTitle));\r
6693   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6694   \r
6695   if (modal) {\r
6696     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6697   } else {\r
6698     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6699     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6700                  hwndMain, (DLGPROC)lpProc);\r
6701     FreeProcInstance(lpProc);\r
6702   }\r
6703 }\r
6704 \r
6705 VOID\r
6706 ErrorPopDown()\r
6707 {\r
6708   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6709   if (errorDialog == NULL) return;\r
6710   DestroyWindow(errorDialog);\r
6711   errorDialog = NULL;\r
6712   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6713 }\r
6714 \r
6715 LRESULT CALLBACK\r
6716 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6717 {\r
6718   HANDLE hwndText;\r
6719   RECT rChild;\r
6720 \r
6721   switch (message) {\r
6722   case WM_INITDIALOG:\r
6723     GetWindowRect(hDlg, &rChild);\r
6724 \r
6725     /*\r
6726     SetWindowPos(hDlg, NULL, rChild.left,\r
6727       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6728       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6729     */\r
6730 \r
6731     /* \r
6732         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6733         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6734         and it doesn't work when you resize the dialog.\r
6735         For now, just give it a default position.\r
6736     */\r
6737     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6738     Translate(hDlg, DLG_Error);\r
6739 \r
6740     errorDialog = hDlg;\r
6741     SetWindowText(hDlg, errorTitle);\r
6742     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6743     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6744     return FALSE;\r
6745 \r
6746   case WM_COMMAND:\r
6747     switch (LOWORD(wParam)) {\r
6748     case IDOK:\r
6749     case IDCANCEL:\r
6750       if (errorDialog == hDlg) errorDialog = NULL;\r
6751       DestroyWindow(hDlg);\r
6752       return TRUE;\r
6753 \r
6754     default:\r
6755       break;\r
6756     }\r
6757     break;\r
6758   }\r
6759   return FALSE;\r
6760 }\r
6761 \r
6762 #ifdef GOTHIC\r
6763 HWND gothicDialog = NULL;\r
6764 \r
6765 LRESULT CALLBACK\r
6766 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6767 {\r
6768   HANDLE hwndText;\r
6769   RECT rChild;\r
6770   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6771 \r
6772   switch (message) {\r
6773   case WM_INITDIALOG:\r
6774     GetWindowRect(hDlg, &rChild);\r
6775 \r
6776     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6777                                                              SWP_NOZORDER);\r
6778 \r
6779     /* \r
6780         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6781         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6782         and it doesn't work when you resize the dialog.\r
6783         For now, just give it a default position.\r
6784     */\r
6785     gothicDialog = hDlg;\r
6786     SetWindowText(hDlg, errorTitle);\r
6787     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6788     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6789     return FALSE;\r
6790 \r
6791   case WM_COMMAND:\r
6792     switch (LOWORD(wParam)) {\r
6793     case IDOK:\r
6794     case IDCANCEL:\r
6795       if (errorDialog == hDlg) errorDialog = NULL;\r
6796       DestroyWindow(hDlg);\r
6797       return TRUE;\r
6798 \r
6799     default:\r
6800       break;\r
6801     }\r
6802     break;\r
6803   }\r
6804   return FALSE;\r
6805 }\r
6806 \r
6807 VOID\r
6808 GothicPopUp(char *title, VariantClass variant)\r
6809 {\r
6810   FARPROC lpProc;\r
6811   static char *lastTitle;\r
6812 \r
6813   strncpy(errorTitle, title, sizeof(errorTitle));\r
6814   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6815 \r
6816   if(lastTitle != title && gothicDialog != NULL) {\r
6817     DestroyWindow(gothicDialog);\r
6818     gothicDialog = NULL;\r
6819   }\r
6820   if(variant != VariantNormal && gothicDialog == NULL) {\r
6821     title = lastTitle;\r
6822     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6823     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6824                  hwndMain, (DLGPROC)lpProc);\r
6825     FreeProcInstance(lpProc);\r
6826   }\r
6827 }\r
6828 #endif\r
6829 \r
6830 /*---------------------------------------------------------------------------*\\r
6831  *\r
6832  *  Ics Interaction console functions\r
6833  *\r
6834 \*---------------------------------------------------------------------------*/\r
6835 \r
6836 #define HISTORY_SIZE 64\r
6837 static char *history[HISTORY_SIZE];\r
6838 int histIn = 0, histP = 0;\r
6839 \r
6840 VOID\r
6841 SaveInHistory(char *cmd)\r
6842 {\r
6843   if (history[histIn] != NULL) {\r
6844     free(history[histIn]);\r
6845     history[histIn] = NULL;\r
6846   }\r
6847   if (*cmd == NULLCHAR) return;\r
6848   history[histIn] = StrSave(cmd);\r
6849   histIn = (histIn + 1) % HISTORY_SIZE;\r
6850   if (history[histIn] != NULL) {\r
6851     free(history[histIn]);\r
6852     history[histIn] = NULL;\r
6853   }\r
6854   histP = histIn;\r
6855 }\r
6856 \r
6857 char *\r
6858 PrevInHistory(char *cmd)\r
6859 {\r
6860   int newhp;\r
6861   if (histP == histIn) {\r
6862     if (history[histIn] != NULL) free(history[histIn]);\r
6863     history[histIn] = StrSave(cmd);\r
6864   }\r
6865   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6866   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6867   histP = newhp;\r
6868   return history[histP];\r
6869 }\r
6870 \r
6871 char *\r
6872 NextInHistory()\r
6873 {\r
6874   if (histP == histIn) return NULL;\r
6875   histP = (histP + 1) % HISTORY_SIZE;\r
6876   return history[histP];   \r
6877 }\r
6878 \r
6879 HMENU\r
6880 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6881 {\r
6882   HMENU hmenu, h;\r
6883   int i = 0;\r
6884   hmenu = LoadMenu(hInst, "TextMenu");\r
6885   h = GetSubMenu(hmenu, 0);\r
6886   while (e->item) {\r
6887     if (strcmp(e->item, "-") == 0) {\r
6888       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6889     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6890       int flags = MF_STRING, j = 0;\r
6891       if (e->item[0] == '|') {\r
6892         flags |= MF_MENUBARBREAK;\r
6893         j++;\r
6894       }\r
6895       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6896       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6897     }\r
6898     e++;\r
6899     i++;\r
6900   } \r
6901   return hmenu;\r
6902 }\r
6903 \r
6904 WNDPROC consoleTextWindowProc;\r
6905 \r
6906 void\r
6907 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6908 {\r
6909   char buf[MSG_SIZ], name[MSG_SIZ];\r
6910   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6911   CHARRANGE sel;\r
6912 \r
6913   if (!getname) {\r
6914     SetWindowText(hInput, command);\r
6915     if (immediate) {\r
6916       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6917     } else {\r
6918       sel.cpMin = 999999;\r
6919       sel.cpMax = 999999;\r
6920       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6921       SetFocus(hInput);\r
6922     }\r
6923     return;\r
6924   }    \r
6925   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6926   if (sel.cpMin == sel.cpMax) {\r
6927     /* Expand to surrounding word */\r
6928     TEXTRANGE tr;\r
6929     do {\r
6930       tr.chrg.cpMax = sel.cpMin;\r
6931       tr.chrg.cpMin = --sel.cpMin;\r
6932       if (sel.cpMin < 0) break;\r
6933       tr.lpstrText = name;\r
6934       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6935     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6936     sel.cpMin++;\r
6937 \r
6938     do {\r
6939       tr.chrg.cpMin = sel.cpMax;\r
6940       tr.chrg.cpMax = ++sel.cpMax;\r
6941       tr.lpstrText = name;\r
6942       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6943     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6944     sel.cpMax--;\r
6945 \r
6946     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6947       MessageBeep(MB_ICONEXCLAMATION);\r
6948       return;\r
6949     }\r
6950     tr.chrg = sel;\r
6951     tr.lpstrText = name;\r
6952     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6953   } else {\r
6954     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6955       MessageBeep(MB_ICONEXCLAMATION);\r
6956       return;\r
6957     }\r
6958     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6959   }\r
6960   if (immediate) {\r
6961     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6962     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6963     SetWindowText(hInput, buf);\r
6964     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6965   } else {\r
6966     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6967       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6968     SetWindowText(hInput, buf);\r
6969     sel.cpMin = 999999;\r
6970     sel.cpMax = 999999;\r
6971     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6972     SetFocus(hInput);\r
6973   }\r
6974 }\r
6975 \r
6976 LRESULT CALLBACK \r
6977 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6978 {\r
6979   HWND hInput;\r
6980   CHARRANGE sel;\r
6981 \r
6982   switch (message) {\r
6983   case WM_KEYDOWN:\r
6984     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6985     if(wParam=='R') return 0;\r
6986     switch (wParam) {\r
6987     case VK_PRIOR:\r
6988       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6989       return 0;\r
6990     case VK_NEXT:\r
6991       sel.cpMin = 999999;\r
6992       sel.cpMax = 999999;\r
6993       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6994       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6995       return 0;\r
6996     }\r
6997     break;\r
6998   case WM_CHAR:\r
6999    if(wParam != '\022') {\r
7000     if (wParam == '\t') {\r
7001       if (GetKeyState(VK_SHIFT) < 0) {\r
7002         /* shifted */\r
7003         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7004         if (buttonDesc[0].hwnd) {\r
7005           SetFocus(buttonDesc[0].hwnd);\r
7006         } else {\r
7007           SetFocus(hwndMain);\r
7008         }\r
7009       } else {\r
7010         /* unshifted */\r
7011         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7012       }\r
7013     } else {\r
7014       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7015       JAWS_DELETE( SetFocus(hInput); )\r
7016       SendMessage(hInput, message, wParam, lParam);\r
7017     }\r
7018     return 0;\r
7019    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7020    lParam = -1;\r
7021   case WM_RBUTTONDOWN:\r
7022     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7023       /* Move selection here if it was empty */\r
7024       POINT pt;\r
7025       pt.x = LOWORD(lParam);\r
7026       pt.y = HIWORD(lParam);\r
7027       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7028       if (sel.cpMin == sel.cpMax) {\r
7029         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7030         sel.cpMax = sel.cpMin;\r
7031         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7032       }\r
7033       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7034 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7035       POINT pt;\r
7036       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7037       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7038       if (sel.cpMin == sel.cpMax) {\r
7039         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7040         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7041       }\r
7042       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7043         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7044       }\r
7045       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7046       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7047       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7048       MenuPopup(hwnd, pt, hmenu, -1);\r
7049 }\r
7050     }\r
7051     return 0;\r
7052   case WM_RBUTTONUP:\r
7053     if (GetKeyState(VK_SHIFT) & ~1) {\r
7054       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7055         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7056     }\r
7057     return 0;\r
7058   case WM_PASTE:\r
7059     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7060     SetFocus(hInput);\r
7061     return SendMessage(hInput, message, wParam, lParam);\r
7062   case WM_MBUTTONDOWN:\r
7063     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7064   case WM_COMMAND:\r
7065     switch (LOWORD(wParam)) {\r
7066     case IDM_QuickPaste:\r
7067       {\r
7068         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7069         if (sel.cpMin == sel.cpMax) {\r
7070           MessageBeep(MB_ICONEXCLAMATION);\r
7071           return 0;\r
7072         }\r
7073         SendMessage(hwnd, WM_COPY, 0, 0);\r
7074         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7075         SendMessage(hInput, WM_PASTE, 0, 0);\r
7076         SetFocus(hInput);\r
7077         return 0;\r
7078       }\r
7079     case IDM_Cut:\r
7080       SendMessage(hwnd, WM_CUT, 0, 0);\r
7081       return 0;\r
7082     case IDM_Paste:\r
7083       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7084       return 0;\r
7085     case IDM_Copy:\r
7086       SendMessage(hwnd, WM_COPY, 0, 0);\r
7087       return 0;\r
7088     default:\r
7089       {\r
7090         int i = LOWORD(wParam) - IDM_CommandX;\r
7091         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7092             icsTextMenuEntry[i].command != NULL) {\r
7093           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7094                    icsTextMenuEntry[i].getname,\r
7095                    icsTextMenuEntry[i].immediate);\r
7096           return 0;\r
7097         }\r
7098       }\r
7099       break;\r
7100     }\r
7101     break;\r
7102   }\r
7103   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7104 }\r
7105 \r
7106 WNDPROC consoleInputWindowProc;\r
7107 \r
7108 LRESULT CALLBACK\r
7109 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7110 {\r
7111   char buf[MSG_SIZ];\r
7112   char *p;\r
7113   static BOOL sendNextChar = FALSE;\r
7114   static BOOL quoteNextChar = FALSE;\r
7115   InputSource *is = consoleInputSource;\r
7116   CHARFORMAT cf;\r
7117   CHARRANGE sel;\r
7118 \r
7119   switch (message) {\r
7120   case WM_CHAR:\r
7121     if (!appData.localLineEditing || sendNextChar) {\r
7122       is->buf[0] = (CHAR) wParam;\r
7123       is->count = 1;\r
7124       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7125       sendNextChar = FALSE;\r
7126       return 0;\r
7127     }\r
7128     if (quoteNextChar) {\r
7129       buf[0] = (char) wParam;\r
7130       buf[1] = NULLCHAR;\r
7131       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7132       quoteNextChar = FALSE;\r
7133       return 0;\r
7134     }\r
7135     switch (wParam) {\r
7136     case '\r':   /* Enter key */\r
7137       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7138       if (consoleEcho) SaveInHistory(is->buf);\r
7139       is->buf[is->count++] = '\n';\r
7140       is->buf[is->count] = NULLCHAR;\r
7141       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7142       if (consoleEcho) {\r
7143         ConsoleOutput(is->buf, is->count, TRUE);\r
7144       } else if (appData.localLineEditing) {\r
7145         ConsoleOutput("\n", 1, TRUE);\r
7146       }\r
7147       /* fall thru */\r
7148     case '\033': /* Escape key */\r
7149       SetWindowText(hwnd, "");\r
7150       cf.cbSize = sizeof(CHARFORMAT);\r
7151       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7152       if (consoleEcho) {\r
7153         cf.crTextColor = textAttribs[ColorNormal].color;\r
7154       } else {\r
7155         cf.crTextColor = COLOR_ECHOOFF;\r
7156       }\r
7157       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7158       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7159       return 0;\r
7160     case '\t':   /* Tab key */\r
7161       if (GetKeyState(VK_SHIFT) < 0) {\r
7162         /* shifted */\r
7163         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7164       } else {\r
7165         /* unshifted */\r
7166         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7167         if (buttonDesc[0].hwnd) {\r
7168           SetFocus(buttonDesc[0].hwnd);\r
7169         } else {\r
7170           SetFocus(hwndMain);\r
7171         }\r
7172       }\r
7173       return 0;\r
7174     case '\023': /* Ctrl+S */\r
7175       sendNextChar = TRUE;\r
7176       return 0;\r
7177     case '\021': /* Ctrl+Q */\r
7178       quoteNextChar = TRUE;\r
7179       return 0;\r
7180     JAWS_REPLAY\r
7181     default:\r
7182       break;\r
7183     }\r
7184     break;\r
7185   case WM_KEYDOWN:\r
7186     switch (wParam) {\r
7187     case VK_UP:\r
7188       GetWindowText(hwnd, buf, MSG_SIZ);\r
7189       p = PrevInHistory(buf);\r
7190       if (p != NULL) {\r
7191         SetWindowText(hwnd, p);\r
7192         sel.cpMin = 999999;\r
7193         sel.cpMax = 999999;\r
7194         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7195         return 0;\r
7196       }\r
7197       break;\r
7198     case VK_DOWN:\r
7199       p = NextInHistory();\r
7200       if (p != NULL) {\r
7201         SetWindowText(hwnd, p);\r
7202         sel.cpMin = 999999;\r
7203         sel.cpMax = 999999;\r
7204         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7205         return 0;\r
7206       }\r
7207       break;\r
7208     case VK_HOME:\r
7209     case VK_END:\r
7210       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7211       /* fall thru */\r
7212     case VK_PRIOR:\r
7213     case VK_NEXT:\r
7214       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7215       return 0;\r
7216     }\r
7217     break;\r
7218   case WM_MBUTTONDOWN:\r
7219     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7220       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7221     break;\r
7222   case WM_RBUTTONUP:\r
7223     if (GetKeyState(VK_SHIFT) & ~1) {\r
7224       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7225         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7226     } else {\r
7227       POINT pt;\r
7228       HMENU hmenu;\r
7229       hmenu = LoadMenu(hInst, "InputMenu");\r
7230       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7231       if (sel.cpMin == sel.cpMax) {\r
7232         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7233         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7234       }\r
7235       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7236         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7237       }\r
7238       pt.x = LOWORD(lParam);\r
7239       pt.y = HIWORD(lParam);\r
7240       MenuPopup(hwnd, pt, hmenu, -1);\r
7241     }\r
7242     return 0;\r
7243   case WM_COMMAND:\r
7244     switch (LOWORD(wParam)) { \r
7245     case IDM_Undo:\r
7246       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7247       return 0;\r
7248     case IDM_SelectAll:\r
7249       sel.cpMin = 0;\r
7250       sel.cpMax = -1; /*999999?*/\r
7251       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7252       return 0;\r
7253     case IDM_Cut:\r
7254       SendMessage(hwnd, WM_CUT, 0, 0);\r
7255       return 0;\r
7256     case IDM_Paste:\r
7257       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7258       return 0;\r
7259     case IDM_Copy:\r
7260       SendMessage(hwnd, WM_COPY, 0, 0);\r
7261       return 0;\r
7262     }\r
7263     break;\r
7264   }\r
7265   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7266 }\r
7267 \r
7268 #define CO_MAX  100000\r
7269 #define CO_TRIM   1000\r
7270 \r
7271 LRESULT CALLBACK\r
7272 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7273 {\r
7274   static SnapData sd;\r
7275   HWND hText, hInput;\r
7276   RECT rect;\r
7277   static int sizeX, sizeY;\r
7278   int newSizeX, newSizeY;\r
7279   MINMAXINFO *mmi;\r
7280   WORD wMask;\r
7281 \r
7282   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7283   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7284 \r
7285   switch (message) {\r
7286   case WM_NOTIFY:\r
7287     if (((NMHDR*)lParam)->code == EN_LINK)\r
7288     {\r
7289       ENLINK *pLink = (ENLINK*)lParam;\r
7290       if (pLink->msg == WM_LBUTTONUP)\r
7291       {\r
7292         TEXTRANGE tr;\r
7293 \r
7294         tr.chrg = pLink->chrg;\r
7295         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7296         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7297         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7298         free(tr.lpstrText);\r
7299       }\r
7300     }\r
7301     break;\r
7302   case WM_INITDIALOG: /* message: initialize dialog box */\r
7303     hwndConsole = hDlg;\r
7304     SetFocus(hInput);\r
7305     consoleTextWindowProc = (WNDPROC)\r
7306       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7307     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7308     consoleInputWindowProc = (WNDPROC)\r
7309       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7310     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7311     Colorize(ColorNormal, TRUE);\r
7312     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7313     ChangedConsoleFont();\r
7314     GetClientRect(hDlg, &rect);\r
7315     sizeX = rect.right;\r
7316     sizeY = rect.bottom;\r
7317     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7318         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7319       WINDOWPLACEMENT wp;\r
7320       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7321       wp.length = sizeof(WINDOWPLACEMENT);\r
7322       wp.flags = 0;\r
7323       wp.showCmd = SW_SHOW;\r
7324       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7325       wp.rcNormalPosition.left = wpConsole.x;\r
7326       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7327       wp.rcNormalPosition.top = wpConsole.y;\r
7328       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7329       SetWindowPlacement(hDlg, &wp);\r
7330     }\r
7331 \r
7332    // [HGM] Chessknight's change 2004-07-13\r
7333    else { /* Determine Defaults */\r
7334        WINDOWPLACEMENT wp;\r
7335        wpConsole.x = wpMain.width + 1;\r
7336        wpConsole.y = wpMain.y;\r
7337        wpConsole.width = screenWidth -  wpMain.width;\r
7338        wpConsole.height = wpMain.height;\r
7339        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7340        wp.length = sizeof(WINDOWPLACEMENT);\r
7341        wp.flags = 0;\r
7342        wp.showCmd = SW_SHOW;\r
7343        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7344        wp.rcNormalPosition.left = wpConsole.x;\r
7345        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7346        wp.rcNormalPosition.top = wpConsole.y;\r
7347        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7348        SetWindowPlacement(hDlg, &wp);\r
7349     }\r
7350 \r
7351    // Allow hText to highlight URLs and send notifications on them\r
7352    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7353    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7354    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7355    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7356 \r
7357     return FALSE;\r
7358 \r
7359   case WM_SETFOCUS:\r
7360     SetFocus(hInput);\r
7361     return 0;\r
7362 \r
7363   case WM_CLOSE:\r
7364     ExitEvent(0);\r
7365     /* not reached */\r
7366     break;\r
7367 \r
7368   case WM_SIZE:\r
7369     if (IsIconic(hDlg)) break;\r
7370     newSizeX = LOWORD(lParam);\r
7371     newSizeY = HIWORD(lParam);\r
7372     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7373       RECT rectText, rectInput;\r
7374       POINT pt;\r
7375       int newTextHeight, newTextWidth;\r
7376       GetWindowRect(hText, &rectText);\r
7377       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7378       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7379       if (newTextHeight < 0) {\r
7380         newSizeY += -newTextHeight;\r
7381         newTextHeight = 0;\r
7382       }\r
7383       SetWindowPos(hText, NULL, 0, 0,\r
7384         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7385       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7386       pt.x = rectInput.left;\r
7387       pt.y = rectInput.top + newSizeY - sizeY;\r
7388       ScreenToClient(hDlg, &pt);\r
7389       SetWindowPos(hInput, NULL, \r
7390         pt.x, pt.y, /* needs client coords */   \r
7391         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7392         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7393     }\r
7394     sizeX = newSizeX;\r
7395     sizeY = newSizeY;\r
7396     break;\r
7397 \r
7398   case WM_GETMINMAXINFO:\r
7399     /* Prevent resizing window too small */\r
7400     mmi = (MINMAXINFO *) lParam;\r
7401     mmi->ptMinTrackSize.x = 100;\r
7402     mmi->ptMinTrackSize.y = 100;\r
7403     break;\r
7404 \r
7405   /* [AS] Snapping */\r
7406   case WM_ENTERSIZEMOVE:\r
7407     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7408 \r
7409   case WM_SIZING:\r
7410     return OnSizing( &sd, hDlg, wParam, lParam );\r
7411 \r
7412   case WM_MOVING:\r
7413     return OnMoving( &sd, hDlg, wParam, lParam );\r
7414 \r
7415   case WM_EXITSIZEMOVE:\r
7416         UpdateICSWidth(hText);\r
7417     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7418   }\r
7419 \r
7420   return DefWindowProc(hDlg, message, wParam, lParam);\r
7421 }\r
7422 \r
7423 \r
7424 VOID\r
7425 ConsoleCreate()\r
7426 {\r
7427   HWND hCons;\r
7428   if (hwndConsole) return;\r
7429   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7430   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7431 }\r
7432 \r
7433 \r
7434 VOID\r
7435 ConsoleOutput(char* data, int length, int forceVisible)\r
7436 {\r
7437   HWND hText;\r
7438   int trim, exlen;\r
7439   char *p, *q;\r
7440   char buf[CO_MAX+1];\r
7441   POINT pEnd;\r
7442   RECT rect;\r
7443   static int delayLF = 0;\r
7444   CHARRANGE savesel, sel;\r
7445 \r
7446   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7447   p = data;\r
7448   q = buf;\r
7449   if (delayLF) {\r
7450     *q++ = '\r';\r
7451     *q++ = '\n';\r
7452     delayLF = 0;\r
7453   }\r
7454   while (length--) {\r
7455     if (*p == '\n') {\r
7456       if (*++p) {\r
7457         *q++ = '\r';\r
7458         *q++ = '\n';\r
7459       } else {\r
7460         delayLF = 1;\r
7461       }\r
7462     } else if (*p == '\007') {\r
7463        MyPlaySound(&sounds[(int)SoundBell]);\r
7464        p++;\r
7465     } else {\r
7466       *q++ = *p++;\r
7467     }\r
7468   }\r
7469   *q = NULLCHAR;\r
7470   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7471   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7472   /* Save current selection */\r
7473   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7474   exlen = GetWindowTextLength(hText);\r
7475   /* Find out whether current end of text is visible */\r
7476   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7477   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7478   /* Trim existing text if it's too long */\r
7479   if (exlen + (q - buf) > CO_MAX) {\r
7480     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7481     sel.cpMin = 0;\r
7482     sel.cpMax = trim;\r
7483     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7484     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7485     exlen -= trim;\r
7486     savesel.cpMin -= trim;\r
7487     savesel.cpMax -= trim;\r
7488     if (exlen < 0) exlen = 0;\r
7489     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7490     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7491   }\r
7492   /* Append the new text */\r
7493   sel.cpMin = exlen;\r
7494   sel.cpMax = exlen;\r
7495   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7496   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7497   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7498   if (forceVisible || exlen == 0 ||\r
7499       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7500        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7501     /* Scroll to make new end of text visible if old end of text\r
7502        was visible or new text is an echo of user typein */\r
7503     sel.cpMin = 9999999;\r
7504     sel.cpMax = 9999999;\r
7505     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7506     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7507     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7508     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7509   }\r
7510   if (savesel.cpMax == exlen || forceVisible) {\r
7511     /* Move insert point to new end of text if it was at the old\r
7512        end of text or if the new text is an echo of user typein */\r
7513     sel.cpMin = 9999999;\r
7514     sel.cpMax = 9999999;\r
7515     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7516   } else {\r
7517     /* Restore previous selection */\r
7518     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7519   }\r
7520   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7521 }\r
7522 \r
7523 /*---------*/\r
7524 \r
7525 \r
7526 void\r
7527 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7528 {\r
7529   char buf[100];\r
7530   char *str;\r
7531   COLORREF oldFg, oldBg;\r
7532   HFONT oldFont;\r
7533   RECT rect;\r
7534 \r
7535   if(copyNumber > 1)\r
7536     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7537 \r
7538   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7539   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7540   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7541 \r
7542   rect.left = x;\r
7543   rect.right = x + squareSize;\r
7544   rect.top  = y;\r
7545   rect.bottom = y + squareSize;\r
7546   str = buf;\r
7547 \r
7548   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7549                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7550              y, ETO_CLIPPED|ETO_OPAQUE,\r
7551              &rect, str, strlen(str), NULL);\r
7552 \r
7553   (void) SetTextColor(hdc, oldFg);\r
7554   (void) SetBkColor(hdc, oldBg);\r
7555   (void) SelectObject(hdc, oldFont);\r
7556 }\r
7557 \r
7558 void\r
7559 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7560               RECT *rect, char *color, char *flagFell)\r
7561 {\r
7562   char buf[100];\r
7563   char *str;\r
7564   COLORREF oldFg, oldBg;\r
7565   HFONT oldFont;\r
7566 \r
7567   if (twoBoards && partnerUp) return;\r
7568   if (appData.clockMode) {\r
7569     if (tinyLayout)\r
7570       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7571     else\r
7572       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7573     str = buf;\r
7574   } else {\r
7575     str = color;\r
7576   }\r
7577 \r
7578   if (highlight) {\r
7579     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7580     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7581   } else {\r
7582     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7583     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7584   }\r
7585   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7586 \r
7587   JAWS_SILENCE\r
7588 \r
7589   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7590              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7591              rect, str, strlen(str), NULL);\r
7592   if(logoHeight > 0 && appData.clockMode) {\r
7593       RECT r;\r
7594       str += strlen(color)+2;\r
7595       r.top = rect->top + logoHeight/2;\r
7596       r.left = rect->left;\r
7597       r.right = rect->right;\r
7598       r.bottom = rect->bottom;\r
7599       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7600                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7601                  &r, str, strlen(str), NULL);\r
7602   }\r
7603   (void) SetTextColor(hdc, oldFg);\r
7604   (void) SetBkColor(hdc, oldBg);\r
7605   (void) SelectObject(hdc, oldFont);\r
7606 }\r
7607 \r
7608 \r
7609 int\r
7610 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7611            OVERLAPPED *ovl)\r
7612 {\r
7613   int ok, err;\r
7614 \r
7615   /* [AS]  */\r
7616   if( count <= 0 ) {\r
7617     if (appData.debugMode) {\r
7618       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7619     }\r
7620 \r
7621     return ERROR_INVALID_USER_BUFFER;\r
7622   }\r
7623 \r
7624   ResetEvent(ovl->hEvent);\r
7625   ovl->Offset = ovl->OffsetHigh = 0;\r
7626   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7627   if (ok) {\r
7628     err = NO_ERROR;\r
7629   } else {\r
7630     err = GetLastError();\r
7631     if (err == ERROR_IO_PENDING) {\r
7632       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7633       if (ok)\r
7634         err = NO_ERROR;\r
7635       else\r
7636         err = GetLastError();\r
7637     }\r
7638   }\r
7639   return err;\r
7640 }\r
7641 \r
7642 int\r
7643 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7644             OVERLAPPED *ovl)\r
7645 {\r
7646   int ok, err;\r
7647 \r
7648   ResetEvent(ovl->hEvent);\r
7649   ovl->Offset = ovl->OffsetHigh = 0;\r
7650   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7651   if (ok) {\r
7652     err = NO_ERROR;\r
7653   } else {\r
7654     err = GetLastError();\r
7655     if (err == ERROR_IO_PENDING) {\r
7656       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7657       if (ok)\r
7658         err = NO_ERROR;\r
7659       else\r
7660         err = GetLastError();\r
7661     }\r
7662   }\r
7663   return err;\r
7664 }\r
7665 \r
7666 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7667 void CheckForInputBufferFull( InputSource * is )\r
7668 {\r
7669     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7670         /* Look for end of line */\r
7671         char * p = is->buf;\r
7672         \r
7673         while( p < is->next && *p != '\n' ) {\r
7674             p++;\r
7675         }\r
7676 \r
7677         if( p >= is->next ) {\r
7678             if (appData.debugMode) {\r
7679                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7680             }\r
7681 \r
7682             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7683             is->count = (DWORD) -1;\r
7684             is->next = is->buf;\r
7685         }\r
7686     }\r
7687 }\r
7688 \r
7689 DWORD\r
7690 InputThread(LPVOID arg)\r
7691 {\r
7692   InputSource *is;\r
7693   OVERLAPPED ovl;\r
7694 \r
7695   is = (InputSource *) arg;\r
7696   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7697   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7698   while (is->hThread != NULL) {\r
7699     is->error = DoReadFile(is->hFile, is->next,\r
7700                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7701                            &is->count, &ovl);\r
7702     if (is->error == NO_ERROR) {\r
7703       is->next += is->count;\r
7704     } else {\r
7705       if (is->error == ERROR_BROKEN_PIPE) {\r
7706         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7707         is->count = 0;\r
7708       } else {\r
7709         is->count = (DWORD) -1;\r
7710         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7711         break; \r
7712       }\r
7713     }\r
7714 \r
7715     CheckForInputBufferFull( is );\r
7716 \r
7717     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7718 \r
7719     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7720 \r
7721     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7722   }\r
7723 \r
7724   CloseHandle(ovl.hEvent);\r
7725   CloseHandle(is->hFile);\r
7726 \r
7727   if (appData.debugMode) {\r
7728     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7729   }\r
7730 \r
7731   return 0;\r
7732 }\r
7733 \r
7734 \r
7735 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7736 DWORD\r
7737 NonOvlInputThread(LPVOID arg)\r
7738 {\r
7739   InputSource *is;\r
7740   char *p, *q;\r
7741   int i;\r
7742   char prev;\r
7743 \r
7744   is = (InputSource *) arg;\r
7745   while (is->hThread != NULL) {\r
7746     is->error = ReadFile(is->hFile, is->next,\r
7747                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7748                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7749     if (is->error == NO_ERROR) {\r
7750       /* Change CRLF to LF */\r
7751       if (is->next > is->buf) {\r
7752         p = is->next - 1;\r
7753         i = is->count + 1;\r
7754       } else {\r
7755         p = is->next;\r
7756         i = is->count;\r
7757       }\r
7758       q = p;\r
7759       prev = NULLCHAR;\r
7760       while (i > 0) {\r
7761         if (prev == '\r' && *p == '\n') {\r
7762           *(q-1) = '\n';\r
7763           is->count--;\r
7764         } else { \r
7765           *q++ = *p;\r
7766         }\r
7767         prev = *p++;\r
7768         i--;\r
7769       }\r
7770       *q = NULLCHAR;\r
7771       is->next = q;\r
7772     } else {\r
7773       if (is->error == ERROR_BROKEN_PIPE) {\r
7774         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7775         is->count = 0; \r
7776       } else {\r
7777         is->count = (DWORD) -1;\r
7778       }\r
7779     }\r
7780 \r
7781     CheckForInputBufferFull( is );\r
7782 \r
7783     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7784 \r
7785     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7786 \r
7787     if (is->count < 0) break;  /* Quit on error */\r
7788   }\r
7789   CloseHandle(is->hFile);\r
7790   return 0;\r
7791 }\r
7792 \r
7793 DWORD\r
7794 SocketInputThread(LPVOID arg)\r
7795 {\r
7796   InputSource *is;\r
7797 \r
7798   is = (InputSource *) arg;\r
7799   while (is->hThread != NULL) {\r
7800     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7801     if ((int)is->count == SOCKET_ERROR) {\r
7802       is->count = (DWORD) -1;\r
7803       is->error = WSAGetLastError();\r
7804     } else {\r
7805       is->error = NO_ERROR;\r
7806       is->next += is->count;\r
7807       if (is->count == 0 && is->second == is) {\r
7808         /* End of file on stderr; quit with no message */\r
7809         break;\r
7810       }\r
7811     }\r
7812     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7813 \r
7814     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7815 \r
7816     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7817   }\r
7818   return 0;\r
7819 }\r
7820 \r
7821 VOID\r
7822 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7823 {\r
7824   InputSource *is;\r
7825 \r
7826   is = (InputSource *) lParam;\r
7827   if (is->lineByLine) {\r
7828     /* Feed in lines one by one */\r
7829     char *p = is->buf;\r
7830     char *q = p;\r
7831     while (q < is->next) {\r
7832       if (*q++ == '\n') {\r
7833         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7834         p = q;\r
7835       }\r
7836     }\r
7837     \r
7838     /* Move any partial line to the start of the buffer */\r
7839     q = is->buf;\r
7840     while (p < is->next) {\r
7841       *q++ = *p++;\r
7842     }\r
7843     is->next = q;\r
7844 \r
7845     if (is->error != NO_ERROR || is->count == 0) {\r
7846       /* Notify backend of the error.  Note: If there was a partial\r
7847          line at the end, it is not flushed through. */\r
7848       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7849     }\r
7850   } else {\r
7851     /* Feed in the whole chunk of input at once */\r
7852     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7853     is->next = is->buf;\r
7854   }\r
7855 }\r
7856 \r
7857 /*---------------------------------------------------------------------------*\\r
7858  *\r
7859  *  Menu enables. Used when setting various modes.\r
7860  *\r
7861 \*---------------------------------------------------------------------------*/\r
7862 \r
7863 typedef struct {\r
7864   int item;\r
7865   int flags;\r
7866 } Enables;\r
7867 \r
7868 VOID\r
7869 GreyRevert(Boolean grey)\r
7870 { // [HGM] vari: for retracting variations in local mode\r
7871   HMENU hmenu = GetMenu(hwndMain);\r
7872   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7873   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7874 }\r
7875 \r
7876 VOID\r
7877 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7878 {\r
7879   while (enab->item > 0) {\r
7880     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7881     enab++;\r
7882   }\r
7883 }\r
7884 \r
7885 Enables gnuEnables[] = {\r
7886   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7899 \r
7900   // Needed to switch from ncp to GNU mode on Engine Load\r
7901   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7902   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7915   { -1, -1 }\r
7916 };\r
7917 \r
7918 Enables icsEnables[] = {\r
7919   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7925   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7929   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7935   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7939   { -1, -1 }\r
7940 };\r
7941 \r
7942 #if ZIPPY\r
7943 Enables zippyEnables[] = {\r
7944   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7945   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7946   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7948   { -1, -1 }\r
7949 };\r
7950 #endif\r
7951 \r
7952 Enables ncpEnables[] = {\r
7953   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7962   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7963   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7975   { -1, -1 }\r
7976 };\r
7977 \r
7978 Enables trainingOnEnables[] = {\r
7979   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7981   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7986   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7987   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7988   { -1, -1 }\r
7989 };\r
7990 \r
7991 Enables trainingOffEnables[] = {\r
7992   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7993   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7994   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7995   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7996   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7997   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7998   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7999   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8000   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8001   { -1, -1 }\r
8002 };\r
8003 \r
8004 /* These modify either ncpEnables or gnuEnables */\r
8005 Enables cmailEnables[] = {\r
8006   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8007   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8008   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8009   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8011   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8013   { -1, -1 }\r
8014 };\r
8015 \r
8016 Enables machineThinkingEnables[] = {\r
8017   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8018   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8019   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8020   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8027   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8028   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8030 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8033   { -1, -1 }\r
8034 };\r
8035 \r
8036 Enables userThinkingEnables[] = {\r
8037   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8042   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8043   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8044   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8045   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8046   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8047   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8048   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8049   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8050 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8051   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8052   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8053   { -1, -1 }\r
8054 };\r
8055 \r
8056 /*---------------------------------------------------------------------------*\\r
8057  *\r
8058  *  Front-end interface functions exported by XBoard.\r
8059  *  Functions appear in same order as prototypes in frontend.h.\r
8060  * \r
8061 \*---------------------------------------------------------------------------*/\r
8062 VOID\r
8063 CheckMark(UINT item, int state)\r
8064 {\r
8065     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8066 }\r
8067 \r
8068 VOID\r
8069 ModeHighlight()\r
8070 {\r
8071   static UINT prevChecked = 0;\r
8072   static int prevPausing = 0;\r
8073   UINT nowChecked;\r
8074 \r
8075   if (pausing != prevPausing) {\r
8076     prevPausing = pausing;\r
8077     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8078                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8079     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8080   }\r
8081 \r
8082   switch (gameMode) {\r
8083   case BeginningOfGame:\r
8084     if (appData.icsActive)\r
8085       nowChecked = IDM_IcsClient;\r
8086     else if (appData.noChessProgram)\r
8087       nowChecked = IDM_EditGame;\r
8088     else\r
8089       nowChecked = IDM_MachineBlack;\r
8090     break;\r
8091   case MachinePlaysBlack:\r
8092     nowChecked = IDM_MachineBlack;\r
8093     break;\r
8094   case MachinePlaysWhite:\r
8095     nowChecked = IDM_MachineWhite;\r
8096     break;\r
8097   case TwoMachinesPlay:\r
8098     nowChecked = IDM_TwoMachines;\r
8099     break;\r
8100   case AnalyzeMode:\r
8101     nowChecked = IDM_AnalysisMode;\r
8102     break;\r
8103   case AnalyzeFile:\r
8104     nowChecked = IDM_AnalyzeFile;\r
8105     break;\r
8106   case EditGame:\r
8107     nowChecked = IDM_EditGame;\r
8108     break;\r
8109   case PlayFromGameFile:\r
8110     nowChecked = IDM_LoadGame;\r
8111     break;\r
8112   case EditPosition:\r
8113     nowChecked = IDM_EditPosition;\r
8114     break;\r
8115   case Training:\r
8116     nowChecked = IDM_Training;\r
8117     break;\r
8118   case IcsPlayingWhite:\r
8119   case IcsPlayingBlack:\r
8120   case IcsObserving:\r
8121   case IcsIdle:\r
8122     nowChecked = IDM_IcsClient;\r
8123     break;\r
8124   default:\r
8125   case EndOfGame:\r
8126     nowChecked = 0;\r
8127     break;\r
8128   }\r
8129   CheckMark(prevChecked, MF_UNCHECKED);\r
8130   CheckMark(nowChecked, MF_CHECKED);\r
8131   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8132 \r
8133   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8134     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8135                           MF_BYCOMMAND|MF_ENABLED);\r
8136   } else {\r
8137     (void) EnableMenuItem(GetMenu(hwndMain), \r
8138                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8139   }\r
8140 \r
8141   prevChecked = nowChecked;\r
8142 \r
8143   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8144   if (appData.icsActive) {\r
8145        if (appData.icsEngineAnalyze) {\r
8146                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8147        } else {\r
8148                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8149        }\r
8150   }\r
8151   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8152 }\r
8153 \r
8154 VOID\r
8155 SetICSMode()\r
8156 {\r
8157   HMENU hmenu = GetMenu(hwndMain);\r
8158   SetMenuEnables(hmenu, icsEnables);\r
8159   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8160     MF_BYCOMMAND|MF_ENABLED);\r
8161 #if ZIPPY\r
8162   if (appData.zippyPlay) {\r
8163     SetMenuEnables(hmenu, zippyEnables);\r
8164     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8165          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8166           MF_BYCOMMAND|MF_ENABLED);\r
8167   }\r
8168 #endif\r
8169 }\r
8170 \r
8171 VOID\r
8172 SetGNUMode()\r
8173 {\r
8174   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8175 }\r
8176 \r
8177 VOID\r
8178 SetNCPMode()\r
8179 {\r
8180   HMENU hmenu = GetMenu(hwndMain);\r
8181   SetMenuEnables(hmenu, ncpEnables);\r
8182     DrawMenuBar(hwndMain);\r
8183 }\r
8184 \r
8185 VOID\r
8186 SetCmailMode()\r
8187 {\r
8188   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8189 }\r
8190 \r
8191 VOID \r
8192 SetTrainingModeOn()\r
8193 {\r
8194   int i;\r
8195   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8196   for (i = 0; i < N_BUTTONS; i++) {\r
8197     if (buttonDesc[i].hwnd != NULL)\r
8198       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8199   }\r
8200   CommentPopDown();\r
8201 }\r
8202 \r
8203 VOID SetTrainingModeOff()\r
8204 {\r
8205   int i;\r
8206   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8207   for (i = 0; i < N_BUTTONS; i++) {\r
8208     if (buttonDesc[i].hwnd != NULL)\r
8209       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8210   }\r
8211 }\r
8212 \r
8213 \r
8214 VOID\r
8215 SetUserThinkingEnables()\r
8216 {\r
8217   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8218 }\r
8219 \r
8220 VOID\r
8221 SetMachineThinkingEnables()\r
8222 {\r
8223   HMENU hMenu = GetMenu(hwndMain);\r
8224   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8225 \r
8226   SetMenuEnables(hMenu, machineThinkingEnables);\r
8227 \r
8228   if (gameMode == MachinePlaysBlack) {\r
8229     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8230   } else if (gameMode == MachinePlaysWhite) {\r
8231     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8232   } else if (gameMode == TwoMachinesPlay) {\r
8233     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8234   }\r
8235 }\r
8236 \r
8237 \r
8238 VOID\r
8239 DisplayTitle(char *str)\r
8240 {\r
8241   char title[MSG_SIZ], *host;\r
8242   if (str[0] != NULLCHAR) {\r
8243     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8244   } else if (appData.icsActive) {\r
8245     if (appData.icsCommPort[0] != NULLCHAR)\r
8246       host = "ICS";\r
8247     else \r
8248       host = appData.icsHost;\r
8249       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8250   } else if (appData.noChessProgram) {\r
8251     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8252   } else {\r
8253     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8254     strcat(title, ": ");\r
8255     strcat(title, first.tidy);\r
8256   }\r
8257   SetWindowText(hwndMain, title);\r
8258 }\r
8259 \r
8260 \r
8261 VOID\r
8262 DisplayMessage(char *str1, char *str2)\r
8263 {\r
8264   HDC hdc;\r
8265   HFONT oldFont;\r
8266   int remain = MESSAGE_TEXT_MAX - 1;\r
8267   int len;\r
8268 \r
8269   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8270   messageText[0] = NULLCHAR;\r
8271   if (*str1) {\r
8272     len = strlen(str1);\r
8273     if (len > remain) len = remain;\r
8274     strncpy(messageText, str1, len);\r
8275     messageText[len] = NULLCHAR;\r
8276     remain -= len;\r
8277   }\r
8278   if (*str2 && remain >= 2) {\r
8279     if (*str1) {\r
8280       strcat(messageText, "  ");\r
8281       remain -= 2;\r
8282     }\r
8283     len = strlen(str2);\r
8284     if (len > remain) len = remain;\r
8285     strncat(messageText, str2, len);\r
8286   }\r
8287   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8288   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8289 \r
8290   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8291 \r
8292   SAYMACHINEMOVE();\r
8293 \r
8294   hdc = GetDC(hwndMain);\r
8295   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8296   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8297              &messageRect, messageText, strlen(messageText), NULL);\r
8298   (void) SelectObject(hdc, oldFont);\r
8299   (void) ReleaseDC(hwndMain, hdc);\r
8300 }\r
8301 \r
8302 VOID\r
8303 DisplayError(char *str, int error)\r
8304 {\r
8305   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8306   int len;\r
8307 \r
8308   if (error == 0) {\r
8309     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8310   } else {\r
8311     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8312                         NULL, error, LANG_NEUTRAL,\r
8313                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8314     if (len > 0) {\r
8315       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8316     } else {\r
8317       ErrorMap *em = errmap;\r
8318       while (em->err != 0 && em->err != error) em++;\r
8319       if (em->err != 0) {\r
8320         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8321       } else {\r
8322         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8323       }\r
8324     }\r
8325   }\r
8326   \r
8327   ErrorPopUp(_("Error"), buf);\r
8328 }\r
8329 \r
8330 \r
8331 VOID\r
8332 DisplayMoveError(char *str)\r
8333 {\r
8334   fromX = fromY = -1;\r
8335   ClearHighlights();\r
8336   DrawPosition(FALSE, NULL);\r
8337   if (appData.popupMoveErrors) {\r
8338     ErrorPopUp(_("Error"), str);\r
8339   } else {\r
8340     DisplayMessage(str, "");\r
8341     moveErrorMessageUp = TRUE;\r
8342   }\r
8343 }\r
8344 \r
8345 VOID\r
8346 DisplayFatalError(char *str, int error, int exitStatus)\r
8347 {\r
8348   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8349   int len;\r
8350   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8351 \r
8352   if (error != 0) {\r
8353     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8354                         NULL, error, LANG_NEUTRAL,\r
8355                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8356     if (len > 0) {\r
8357       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8358     } else {\r
8359       ErrorMap *em = errmap;\r
8360       while (em->err != 0 && em->err != error) em++;\r
8361       if (em->err != 0) {\r
8362         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8363       } else {\r
8364         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8365       }\r
8366     }\r
8367     str = buf;\r
8368   }\r
8369   if (appData.debugMode) {\r
8370     fprintf(debugFP, "%s: %s\n", label, str);\r
8371   }\r
8372   if (appData.popupExitMessage) {\r
8373     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8374                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8375   }\r
8376   ExitEvent(exitStatus);\r
8377 }\r
8378 \r
8379 \r
8380 VOID\r
8381 DisplayInformation(char *str)\r
8382 {\r
8383   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8384 }\r
8385 \r
8386 \r
8387 VOID\r
8388 DisplayNote(char *str)\r
8389 {\r
8390   ErrorPopUp(_("Note"), str);\r
8391 }\r
8392 \r
8393 \r
8394 typedef struct {\r
8395   char *title, *question, *replyPrefix;\r
8396   ProcRef pr;\r
8397 } QuestionParams;\r
8398 \r
8399 LRESULT CALLBACK\r
8400 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8401 {\r
8402   static QuestionParams *qp;\r
8403   char reply[MSG_SIZ];\r
8404   int len, err;\r
8405 \r
8406   switch (message) {\r
8407   case WM_INITDIALOG:\r
8408     qp = (QuestionParams *) lParam;\r
8409     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8410     Translate(hDlg, DLG_Question);\r
8411     SetWindowText(hDlg, qp->title);\r
8412     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8413     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8414     return FALSE;\r
8415 \r
8416   case WM_COMMAND:\r
8417     switch (LOWORD(wParam)) {\r
8418     case IDOK:\r
8419       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8420       if (*reply) strcat(reply, " ");\r
8421       len = strlen(reply);\r
8422       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8423       strcat(reply, "\n");\r
8424       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8425       EndDialog(hDlg, TRUE);\r
8426       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8427       return TRUE;\r
8428     case IDCANCEL:\r
8429       EndDialog(hDlg, FALSE);\r
8430       return TRUE;\r
8431     default:\r
8432       break;\r
8433     }\r
8434     break;\r
8435   }\r
8436   return FALSE;\r
8437 }\r
8438 \r
8439 VOID\r
8440 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8441 {\r
8442     QuestionParams qp;\r
8443     FARPROC lpProc;\r
8444     \r
8445     qp.title = title;\r
8446     qp.question = question;\r
8447     qp.replyPrefix = replyPrefix;\r
8448     qp.pr = pr;\r
8449     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8450     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8451       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8452     FreeProcInstance(lpProc);\r
8453 }\r
8454 \r
8455 /* [AS] Pick FRC position */\r
8456 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8457 {\r
8458     static int * lpIndexFRC;\r
8459     BOOL index_is_ok;\r
8460     char buf[16];\r
8461 \r
8462     switch( message )\r
8463     {\r
8464     case WM_INITDIALOG:\r
8465         lpIndexFRC = (int *) lParam;\r
8466 \r
8467         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8468         Translate(hDlg, DLG_NewGameFRC);\r
8469 \r
8470         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8471         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8472         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8473         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8474 \r
8475         break;\r
8476 \r
8477     case WM_COMMAND:\r
8478         switch( LOWORD(wParam) ) {\r
8479         case IDOK:\r
8480             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8481             EndDialog( hDlg, 0 );\r
8482             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8483             return TRUE;\r
8484         case IDCANCEL:\r
8485             EndDialog( hDlg, 1 );   \r
8486             return TRUE;\r
8487         case IDC_NFG_Edit:\r
8488             if( HIWORD(wParam) == EN_CHANGE ) {\r
8489                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8490 \r
8491                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8492             }\r
8493             return TRUE;\r
8494         case IDC_NFG_Random:\r
8495           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8496             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8497             return TRUE;\r
8498         }\r
8499 \r
8500         break;\r
8501     }\r
8502 \r
8503     return FALSE;\r
8504 }\r
8505 \r
8506 int NewGameFRC()\r
8507 {\r
8508     int result;\r
8509     int index = appData.defaultFrcPosition;\r
8510     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8511 \r
8512     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8513 \r
8514     if( result == 0 ) {\r
8515         appData.defaultFrcPosition = index;\r
8516     }\r
8517 \r
8518     return result;\r
8519 }\r
8520 \r
8521 /* [AS] Game list options. Refactored by HGM */\r
8522 \r
8523 HWND gameListOptionsDialog;\r
8524 \r
8525 // low-level front-end: clear text edit / list widget\r
8526 void\r
8527 GLT_ClearList()\r
8528 {\r
8529     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8530 }\r
8531 \r
8532 // low-level front-end: clear text edit / list widget\r
8533 void\r
8534 GLT_DeSelectList()\r
8535 {\r
8536     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8537 }\r
8538 \r
8539 // low-level front-end: append line to text edit / list widget\r
8540 void\r
8541 GLT_AddToList( char *name )\r
8542 {\r
8543     if( name != 0 ) {\r
8544             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8545     }\r
8546 }\r
8547 \r
8548 // low-level front-end: get line from text edit / list widget\r
8549 Boolean\r
8550 GLT_GetFromList( int index, char *name )\r
8551 {\r
8552     if( name != 0 ) {\r
8553             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8554                 return TRUE;\r
8555     }\r
8556     return FALSE;\r
8557 }\r
8558 \r
8559 void GLT_MoveSelection( HWND hDlg, int delta )\r
8560 {\r
8561     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8562     int idx2 = idx1 + delta;\r
8563     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8564 \r
8565     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8566         char buf[128];\r
8567 \r
8568         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8569         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8570         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8571         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8572     }\r
8573 }\r
8574 \r
8575 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8576 {\r
8577     switch( message )\r
8578     {\r
8579     case WM_INITDIALOG:\r
8580         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8581         \r
8582         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8583         Translate(hDlg, DLG_GameListOptions);\r
8584 \r
8585         /* Initialize list */\r
8586         GLT_TagsToList( lpUserGLT );\r
8587 \r
8588         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8589 \r
8590         break;\r
8591 \r
8592     case WM_COMMAND:\r
8593         switch( LOWORD(wParam) ) {\r
8594         case IDOK:\r
8595             GLT_ParseList();\r
8596             EndDialog( hDlg, 0 );\r
8597             return TRUE;\r
8598         case IDCANCEL:\r
8599             EndDialog( hDlg, 1 );\r
8600             return TRUE;\r
8601 \r
8602         case IDC_GLT_Default:\r
8603             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8604             return TRUE;\r
8605 \r
8606         case IDC_GLT_Restore:\r
8607             GLT_TagsToList( appData.gameListTags );\r
8608             return TRUE;\r
8609 \r
8610         case IDC_GLT_Up:\r
8611             GLT_MoveSelection( hDlg, -1 );\r
8612             return TRUE;\r
8613 \r
8614         case IDC_GLT_Down:\r
8615             GLT_MoveSelection( hDlg, +1 );\r
8616             return TRUE;\r
8617         }\r
8618 \r
8619         break;\r
8620     }\r
8621 \r
8622     return FALSE;\r
8623 }\r
8624 \r
8625 int GameListOptions()\r
8626 {\r
8627     int result;\r
8628     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8629 \r
8630       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8631 \r
8632     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8633 \r
8634     if( result == 0 ) {\r
8635         /* [AS] Memory leak here! */\r
8636         appData.gameListTags = strdup( lpUserGLT ); \r
8637     }\r
8638 \r
8639     return result;\r
8640 }\r
8641 \r
8642 VOID\r
8643 DisplayIcsInteractionTitle(char *str)\r
8644 {\r
8645   char consoleTitle[MSG_SIZ];\r
8646 \r
8647     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8648     SetWindowText(hwndConsole, consoleTitle);\r
8649 \r
8650     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8651       char buf[MSG_SIZ], *p = buf, *q;\r
8652         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8653       do {\r
8654         q = strchr(p, ';');\r
8655         if(q) *q++ = 0;\r
8656         if(*p) ChatPopUp(p);\r
8657       } while(p=q);\r
8658     }\r
8659 \r
8660     SetActiveWindow(hwndMain);\r
8661 }\r
8662 \r
8663 void\r
8664 DrawPosition(int fullRedraw, Board board)\r
8665 {\r
8666   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8667 }\r
8668 \r
8669 void NotifyFrontendLogin()\r
8670 {\r
8671         if (hwndConsole)\r
8672                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8673 }\r
8674 \r
8675 VOID\r
8676 ResetFrontEnd()\r
8677 {\r
8678   fromX = fromY = -1;\r
8679   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8680     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8681     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8682     dragInfo.lastpos = dragInfo.pos;\r
8683     dragInfo.start.x = dragInfo.start.y = -1;\r
8684     dragInfo.from = dragInfo.start;\r
8685     ReleaseCapture();\r
8686     DrawPosition(TRUE, NULL);\r
8687   }\r
8688   TagsPopDown();\r
8689 }\r
8690 \r
8691 \r
8692 VOID\r
8693 CommentPopUp(char *title, char *str)\r
8694 {\r
8695   HWND hwnd = GetActiveWindow();\r
8696   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8697   SAY(str);\r
8698   SetActiveWindow(hwnd);\r
8699 }\r
8700 \r
8701 VOID\r
8702 CommentPopDown(void)\r
8703 {\r
8704   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8705   if (commentDialog) {\r
8706     ShowWindow(commentDialog, SW_HIDE);\r
8707   }\r
8708   commentUp = FALSE;\r
8709 }\r
8710 \r
8711 VOID\r
8712 EditCommentPopUp(int index, char *title, char *str)\r
8713 {\r
8714   EitherCommentPopUp(index, title, str, TRUE);\r
8715 }\r
8716 \r
8717 \r
8718 VOID\r
8719 RingBell()\r
8720 {\r
8721   MyPlaySound(&sounds[(int)SoundMove]);\r
8722 }\r
8723 \r
8724 VOID PlayIcsWinSound()\r
8725 {\r
8726   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8727 }\r
8728 \r
8729 VOID PlayIcsLossSound()\r
8730 {\r
8731   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8732 }\r
8733 \r
8734 VOID PlayIcsDrawSound()\r
8735 {\r
8736   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8737 }\r
8738 \r
8739 VOID PlayIcsUnfinishedSound()\r
8740 {\r
8741   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8742 }\r
8743 \r
8744 VOID\r
8745 PlayAlarmSound()\r
8746 {\r
8747   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8748 }\r
8749 \r
8750 VOID\r
8751 PlayTellSound()\r
8752 {\r
8753   MyPlaySound(&textAttribs[ColorTell].sound);\r
8754 }\r
8755 \r
8756 \r
8757 VOID\r
8758 EchoOn()\r
8759 {\r
8760   HWND hInput;\r
8761   consoleEcho = TRUE;\r
8762   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8763   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8764   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8765 }\r
8766 \r
8767 \r
8768 VOID\r
8769 EchoOff()\r
8770 {\r
8771   CHARFORMAT cf;\r
8772   HWND hInput;\r
8773   consoleEcho = FALSE;\r
8774   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8775   /* This works OK: set text and background both to the same color */\r
8776   cf = consoleCF;\r
8777   cf.crTextColor = COLOR_ECHOOFF;\r
8778   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8779   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8780 }\r
8781 \r
8782 /* No Raw()...? */\r
8783 \r
8784 void Colorize(ColorClass cc, int continuation)\r
8785 {\r
8786   currentColorClass = cc;\r
8787   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8788   consoleCF.crTextColor = textAttribs[cc].color;\r
8789   consoleCF.dwEffects = textAttribs[cc].effects;\r
8790   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8791 }\r
8792 \r
8793 char *\r
8794 UserName()\r
8795 {\r
8796   static char buf[MSG_SIZ];\r
8797   DWORD bufsiz = MSG_SIZ;\r
8798 \r
8799   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8800         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8801   }\r
8802   if (!GetUserName(buf, &bufsiz)) {\r
8803     /*DisplayError("Error getting user name", GetLastError());*/\r
8804     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8805   }\r
8806   return buf;\r
8807 }\r
8808 \r
8809 char *\r
8810 HostName()\r
8811 {\r
8812   static char buf[MSG_SIZ];\r
8813   DWORD bufsiz = MSG_SIZ;\r
8814 \r
8815   if (!GetComputerName(buf, &bufsiz)) {\r
8816     /*DisplayError("Error getting host name", GetLastError());*/\r
8817     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8818   }\r
8819   return buf;\r
8820 }\r
8821 \r
8822 \r
8823 int\r
8824 ClockTimerRunning()\r
8825 {\r
8826   return clockTimerEvent != 0;\r
8827 }\r
8828 \r
8829 int\r
8830 StopClockTimer()\r
8831 {\r
8832   if (clockTimerEvent == 0) return FALSE;\r
8833   KillTimer(hwndMain, clockTimerEvent);\r
8834   clockTimerEvent = 0;\r
8835   return TRUE;\r
8836 }\r
8837 \r
8838 void\r
8839 StartClockTimer(long millisec)\r
8840 {\r
8841   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8842                              (UINT) millisec, NULL);\r
8843 }\r
8844 \r
8845 void\r
8846 DisplayWhiteClock(long timeRemaining, int highlight)\r
8847 {\r
8848   HDC hdc;\r
8849   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8850 \r
8851   if(appData.noGUI) return;\r
8852   hdc = GetDC(hwndMain);\r
8853   if (!IsIconic(hwndMain)) {\r
8854     DisplayAClock(hdc, timeRemaining, highlight, \r
8855                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8856   }\r
8857   if (highlight && iconCurrent == iconBlack) {\r
8858     iconCurrent = iconWhite;\r
8859     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8860     if (IsIconic(hwndMain)) {\r
8861       DrawIcon(hdc, 2, 2, iconCurrent);\r
8862     }\r
8863   }\r
8864   (void) ReleaseDC(hwndMain, hdc);\r
8865   if (hwndConsole)\r
8866     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8867 }\r
8868 \r
8869 void\r
8870 DisplayBlackClock(long timeRemaining, int highlight)\r
8871 {\r
8872   HDC hdc;\r
8873   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8874 \r
8875   if(appData.noGUI) return;\r
8876   hdc = GetDC(hwndMain);\r
8877   if (!IsIconic(hwndMain)) {\r
8878     DisplayAClock(hdc, timeRemaining, highlight, \r
8879                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8880   }\r
8881   if (highlight && iconCurrent == iconWhite) {\r
8882     iconCurrent = iconBlack;\r
8883     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8884     if (IsIconic(hwndMain)) {\r
8885       DrawIcon(hdc, 2, 2, iconCurrent);\r
8886     }\r
8887   }\r
8888   (void) ReleaseDC(hwndMain, hdc);\r
8889   if (hwndConsole)\r
8890     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8891 }\r
8892 \r
8893 \r
8894 int\r
8895 LoadGameTimerRunning()\r
8896 {\r
8897   return loadGameTimerEvent != 0;\r
8898 }\r
8899 \r
8900 int\r
8901 StopLoadGameTimer()\r
8902 {\r
8903   if (loadGameTimerEvent == 0) return FALSE;\r
8904   KillTimer(hwndMain, loadGameTimerEvent);\r
8905   loadGameTimerEvent = 0;\r
8906   return TRUE;\r
8907 }\r
8908 \r
8909 void\r
8910 StartLoadGameTimer(long millisec)\r
8911 {\r
8912   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8913                                 (UINT) millisec, NULL);\r
8914 }\r
8915 \r
8916 void\r
8917 AutoSaveGame()\r
8918 {\r
8919   char *defName;\r
8920   FILE *f;\r
8921   char fileTitle[MSG_SIZ];\r
8922 \r
8923   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8924   f = OpenFileDialog(hwndMain, "a", defName,\r
8925                      appData.oldSaveStyle ? "gam" : "pgn",\r
8926                      GAME_FILT, \r
8927                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8928   if (f != NULL) {\r
8929     SaveGame(f, 0, "");\r
8930     fclose(f);\r
8931   }\r
8932 }\r
8933 \r
8934 \r
8935 void\r
8936 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8937 {\r
8938   if (delayedTimerEvent != 0) {\r
8939     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8940       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8941     }\r
8942     KillTimer(hwndMain, delayedTimerEvent);\r
8943     delayedTimerEvent = 0;\r
8944     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8945     delayedTimerCallback();\r
8946   }\r
8947   delayedTimerCallback = cb;\r
8948   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8949                                 (UINT) millisec, NULL);\r
8950 }\r
8951 \r
8952 DelayedEventCallback\r
8953 GetDelayedEvent()\r
8954 {\r
8955   if (delayedTimerEvent) {\r
8956     return delayedTimerCallback;\r
8957   } else {\r
8958     return NULL;\r
8959   }\r
8960 }\r
8961 \r
8962 void\r
8963 CancelDelayedEvent()\r
8964 {\r
8965   if (delayedTimerEvent) {\r
8966     KillTimer(hwndMain, delayedTimerEvent);\r
8967     delayedTimerEvent = 0;\r
8968   }\r
8969 }\r
8970 \r
8971 DWORD GetWin32Priority(int nice)\r
8972 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8973 /*\r
8974 REALTIME_PRIORITY_CLASS     0x00000100\r
8975 HIGH_PRIORITY_CLASS         0x00000080\r
8976 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8977 NORMAL_PRIORITY_CLASS       0x00000020\r
8978 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8979 IDLE_PRIORITY_CLASS         0x00000040\r
8980 */\r
8981         if (nice < -15) return 0x00000080;\r
8982         if (nice < 0)   return 0x00008000;\r
8983         if (nice == 0)  return 0x00000020;\r
8984         if (nice < 15)  return 0x00004000;\r
8985         return 0x00000040;\r
8986 }\r
8987 \r
8988 void RunCommand(char *cmdLine)\r
8989 {\r
8990   /* Now create the child process. */\r
8991   STARTUPINFO siStartInfo;\r
8992   PROCESS_INFORMATION piProcInfo;\r
8993 \r
8994   siStartInfo.cb = sizeof(STARTUPINFO);\r
8995   siStartInfo.lpReserved = NULL;\r
8996   siStartInfo.lpDesktop = NULL;\r
8997   siStartInfo.lpTitle = NULL;\r
8998   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8999   siStartInfo.cbReserved2 = 0;\r
9000   siStartInfo.lpReserved2 = NULL;\r
9001   siStartInfo.hStdInput = NULL;\r
9002   siStartInfo.hStdOutput = NULL;\r
9003   siStartInfo.hStdError = NULL;\r
9004 \r
9005   CreateProcess(NULL,\r
9006                 cmdLine,           /* command line */\r
9007                 NULL,      /* process security attributes */\r
9008                 NULL,      /* primary thread security attrs */\r
9009                 TRUE,      /* handles are inherited */\r
9010                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9011                 NULL,      /* use parent's environment */\r
9012                 NULL,\r
9013                 &siStartInfo, /* STARTUPINFO pointer */\r
9014                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9015 \r
9016   CloseHandle(piProcInfo.hThread);\r
9017 }\r
9018 \r
9019 /* Start a child process running the given program.\r
9020    The process's standard output can be read from "from", and its\r
9021    standard input can be written to "to".\r
9022    Exit with fatal error if anything goes wrong.\r
9023    Returns an opaque pointer that can be used to destroy the process\r
9024    later.\r
9025 */\r
9026 int\r
9027 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9028 {\r
9029 #define BUFSIZE 4096\r
9030 \r
9031   HANDLE hChildStdinRd, hChildStdinWr,\r
9032     hChildStdoutRd, hChildStdoutWr;\r
9033   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9034   SECURITY_ATTRIBUTES saAttr;\r
9035   BOOL fSuccess;\r
9036   PROCESS_INFORMATION piProcInfo;\r
9037   STARTUPINFO siStartInfo;\r
9038   ChildProc *cp;\r
9039   char buf[MSG_SIZ];\r
9040   DWORD err;\r
9041 \r
9042   if (appData.debugMode) {\r
9043     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9044   }\r
9045 \r
9046   *pr = NoProc;\r
9047 \r
9048   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9049   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9050   saAttr.bInheritHandle = TRUE;\r
9051   saAttr.lpSecurityDescriptor = NULL;\r
9052 \r
9053   /*\r
9054    * The steps for redirecting child's STDOUT:\r
9055    *     1. Create anonymous pipe to be STDOUT for child.\r
9056    *     2. Create a noninheritable duplicate of read handle,\r
9057    *         and close the inheritable read handle.\r
9058    */\r
9059 \r
9060   /* Create a pipe for the child's STDOUT. */\r
9061   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9062     return GetLastError();\r
9063   }\r
9064 \r
9065   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9066   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9067                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9068                              FALSE,     /* not inherited */\r
9069                              DUPLICATE_SAME_ACCESS);\r
9070   if (! fSuccess) {\r
9071     return GetLastError();\r
9072   }\r
9073   CloseHandle(hChildStdoutRd);\r
9074 \r
9075   /*\r
9076    * The steps for redirecting child's STDIN:\r
9077    *     1. Create anonymous pipe to be STDIN for child.\r
9078    *     2. Create a noninheritable duplicate of write handle,\r
9079    *         and close the inheritable write handle.\r
9080    */\r
9081 \r
9082   /* Create a pipe for the child's STDIN. */\r
9083   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9084     return GetLastError();\r
9085   }\r
9086 \r
9087   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9088   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9089                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9090                              FALSE,     /* not inherited */\r
9091                              DUPLICATE_SAME_ACCESS);\r
9092   if (! fSuccess) {\r
9093     return GetLastError();\r
9094   }\r
9095   CloseHandle(hChildStdinWr);\r
9096 \r
9097   /* Arrange to (1) look in dir for the child .exe file, and\r
9098    * (2) have dir be the child's working directory.  Interpret\r
9099    * dir relative to the directory WinBoard loaded from. */\r
9100   GetCurrentDirectory(MSG_SIZ, buf);\r
9101   SetCurrentDirectory(installDir);\r
9102   SetCurrentDirectory(dir);\r
9103 \r
9104   /* Now create the child process. */\r
9105 \r
9106   siStartInfo.cb = sizeof(STARTUPINFO);\r
9107   siStartInfo.lpReserved = NULL;\r
9108   siStartInfo.lpDesktop = NULL;\r
9109   siStartInfo.lpTitle = NULL;\r
9110   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9111   siStartInfo.cbReserved2 = 0;\r
9112   siStartInfo.lpReserved2 = NULL;\r
9113   siStartInfo.hStdInput = hChildStdinRd;\r
9114   siStartInfo.hStdOutput = hChildStdoutWr;\r
9115   siStartInfo.hStdError = hChildStdoutWr;\r
9116 \r
9117   fSuccess = CreateProcess(NULL,\r
9118                            cmdLine,        /* command line */\r
9119                            NULL,           /* process security attributes */\r
9120                            NULL,           /* primary thread security attrs */\r
9121                            TRUE,           /* handles are inherited */\r
9122                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9123                            NULL,           /* use parent's environment */\r
9124                            NULL,\r
9125                            &siStartInfo, /* STARTUPINFO pointer */\r
9126                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9127 \r
9128   err = GetLastError();\r
9129   SetCurrentDirectory(buf); /* return to prev directory */\r
9130   if (! fSuccess) {\r
9131     return err;\r
9132   }\r
9133 \r
9134   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9135     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9136     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9137   }\r
9138 \r
9139   /* Close the handles we don't need in the parent */\r
9140   CloseHandle(piProcInfo.hThread);\r
9141   CloseHandle(hChildStdinRd);\r
9142   CloseHandle(hChildStdoutWr);\r
9143 \r
9144   /* Prepare return value */\r
9145   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9146   cp->kind = CPReal;\r
9147   cp->hProcess = piProcInfo.hProcess;\r
9148   cp->pid = piProcInfo.dwProcessId;\r
9149   cp->hFrom = hChildStdoutRdDup;\r
9150   cp->hTo = hChildStdinWrDup;\r
9151 \r
9152   *pr = (void *) cp;\r
9153 \r
9154   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9155      2000 where engines sometimes don't see the initial command(s)\r
9156      from WinBoard and hang.  I don't understand how that can happen,\r
9157      but the Sleep is harmless, so I've put it in.  Others have also\r
9158      reported what may be the same problem, so hopefully this will fix\r
9159      it for them too.  */\r
9160   Sleep(500);\r
9161 \r
9162   return NO_ERROR;\r
9163 }\r
9164 \r
9165 \r
9166 void\r
9167 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9168 {\r
9169   ChildProc *cp; int result;\r
9170 \r
9171   cp = (ChildProc *) pr;\r
9172   if (cp == NULL) return;\r
9173 \r
9174   switch (cp->kind) {\r
9175   case CPReal:\r
9176     /* TerminateProcess is considered harmful, so... */\r
9177     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9178     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9179     /* The following doesn't work because the chess program\r
9180        doesn't "have the same console" as WinBoard.  Maybe\r
9181        we could arrange for this even though neither WinBoard\r
9182        nor the chess program uses a console for stdio? */\r
9183     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9184 \r
9185     /* [AS] Special termination modes for misbehaving programs... */\r
9186     if( signal == 9 ) { \r
9187         result = TerminateProcess( cp->hProcess, 0 );\r
9188 \r
9189         if ( appData.debugMode) {\r
9190             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9191         }\r
9192     }\r
9193     else if( signal == 10 ) {\r
9194         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9195 \r
9196         if( dw != WAIT_OBJECT_0 ) {\r
9197             result = TerminateProcess( cp->hProcess, 0 );\r
9198 \r
9199             if ( appData.debugMode) {\r
9200                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9201             }\r
9202 \r
9203         }\r
9204     }\r
9205 \r
9206     CloseHandle(cp->hProcess);\r
9207     break;\r
9208 \r
9209   case CPComm:\r
9210     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9211     break;\r
9212 \r
9213   case CPSock:\r
9214     closesocket(cp->sock);\r
9215     WSACleanup();\r
9216     break;\r
9217 \r
9218   case CPRcmd:\r
9219     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9220     closesocket(cp->sock);\r
9221     closesocket(cp->sock2);\r
9222     WSACleanup();\r
9223     break;\r
9224   }\r
9225   free(cp);\r
9226 }\r
9227 \r
9228 void\r
9229 InterruptChildProcess(ProcRef pr)\r
9230 {\r
9231   ChildProc *cp;\r
9232 \r
9233   cp = (ChildProc *) pr;\r
9234   if (cp == NULL) return;\r
9235   switch (cp->kind) {\r
9236   case CPReal:\r
9237     /* The following doesn't work because the chess program\r
9238        doesn't "have the same console" as WinBoard.  Maybe\r
9239        we could arrange for this even though neither WinBoard\r
9240        nor the chess program uses a console for stdio */\r
9241     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9242     break;\r
9243 \r
9244   case CPComm:\r
9245   case CPSock:\r
9246     /* Can't interrupt */\r
9247     break;\r
9248 \r
9249   case CPRcmd:\r
9250     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9251     break;\r
9252   }\r
9253 }\r
9254 \r
9255 \r
9256 int\r
9257 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9258 {\r
9259   char cmdLine[MSG_SIZ];\r
9260 \r
9261   if (port[0] == NULLCHAR) {\r
9262     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9263   } else {\r
9264     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9265   }\r
9266   return StartChildProcess(cmdLine, "", pr);\r
9267 }\r
9268 \r
9269 \r
9270 /* Code to open TCP sockets */\r
9271 \r
9272 int\r
9273 OpenTCP(char *host, char *port, ProcRef *pr)\r
9274 {\r
9275   ChildProc *cp;\r
9276   int err;\r
9277   SOCKET s;\r
9278 \r
9279   struct sockaddr_in sa, mysa;\r
9280   struct hostent FAR *hp;\r
9281   unsigned short uport;\r
9282   WORD wVersionRequested;\r
9283   WSADATA wsaData;\r
9284 \r
9285   /* Initialize socket DLL */\r
9286   wVersionRequested = MAKEWORD(1, 1);\r
9287   err = WSAStartup(wVersionRequested, &wsaData);\r
9288   if (err != 0) return err;\r
9289 \r
9290   /* Make socket */\r
9291   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9292     err = WSAGetLastError();\r
9293     WSACleanup();\r
9294     return err;\r
9295   }\r
9296 \r
9297   /* Bind local address using (mostly) don't-care values.\r
9298    */\r
9299   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9300   mysa.sin_family = AF_INET;\r
9301   mysa.sin_addr.s_addr = INADDR_ANY;\r
9302   uport = (unsigned short) 0;\r
9303   mysa.sin_port = htons(uport);\r
9304   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9305       == SOCKET_ERROR) {\r
9306     err = WSAGetLastError();\r
9307     WSACleanup();\r
9308     return err;\r
9309   }\r
9310 \r
9311   /* Resolve remote host name */\r
9312   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9313   if (!(hp = gethostbyname(host))) {\r
9314     unsigned int b0, b1, b2, b3;\r
9315 \r
9316     err = WSAGetLastError();\r
9317 \r
9318     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9319       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9320       hp->h_addrtype = AF_INET;\r
9321       hp->h_length = 4;\r
9322       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9323       hp->h_addr_list[0] = (char *) malloc(4);\r
9324       hp->h_addr_list[0][0] = (char) b0;\r
9325       hp->h_addr_list[0][1] = (char) b1;\r
9326       hp->h_addr_list[0][2] = (char) b2;\r
9327       hp->h_addr_list[0][3] = (char) b3;\r
9328     } else {\r
9329       WSACleanup();\r
9330       return err;\r
9331     }\r
9332   }\r
9333   sa.sin_family = hp->h_addrtype;\r
9334   uport = (unsigned short) atoi(port);\r
9335   sa.sin_port = htons(uport);\r
9336   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9337 \r
9338   /* Make connection */\r
9339   if (connect(s, (struct sockaddr *) &sa,\r
9340               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9341     err = WSAGetLastError();\r
9342     WSACleanup();\r
9343     return err;\r
9344   }\r
9345 \r
9346   /* Prepare return value */\r
9347   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9348   cp->kind = CPSock;\r
9349   cp->sock = s;\r
9350   *pr = (ProcRef *) cp;\r
9351 \r
9352   return NO_ERROR;\r
9353 }\r
9354 \r
9355 int\r
9356 OpenCommPort(char *name, ProcRef *pr)\r
9357 {\r
9358   HANDLE h;\r
9359   COMMTIMEOUTS ct;\r
9360   ChildProc *cp;\r
9361   char fullname[MSG_SIZ];\r
9362 \r
9363   if (*name != '\\')\r
9364     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9365   else\r
9366     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9367 \r
9368   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9369                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9370   if (h == (HANDLE) -1) {\r
9371     return GetLastError();\r
9372   }\r
9373   hCommPort = h;\r
9374 \r
9375   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9376 \r
9377   /* Accumulate characters until a 100ms pause, then parse */\r
9378   ct.ReadIntervalTimeout = 100;\r
9379   ct.ReadTotalTimeoutMultiplier = 0;\r
9380   ct.ReadTotalTimeoutConstant = 0;\r
9381   ct.WriteTotalTimeoutMultiplier = 0;\r
9382   ct.WriteTotalTimeoutConstant = 0;\r
9383   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9384 \r
9385   /* Prepare return value */\r
9386   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9387   cp->kind = CPComm;\r
9388   cp->hFrom = h;\r
9389   cp->hTo = h;\r
9390   *pr = (ProcRef *) cp;\r
9391 \r
9392   return NO_ERROR;\r
9393 }\r
9394 \r
9395 int\r
9396 OpenLoopback(ProcRef *pr)\r
9397 {\r
9398   DisplayFatalError(_("Not implemented"), 0, 1);\r
9399   return NO_ERROR;\r
9400 }\r
9401 \r
9402 \r
9403 int\r
9404 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9405 {\r
9406   ChildProc *cp;\r
9407   int err;\r
9408   SOCKET s, s2, s3;\r
9409   struct sockaddr_in sa, mysa;\r
9410   struct hostent FAR *hp;\r
9411   unsigned short uport;\r
9412   WORD wVersionRequested;\r
9413   WSADATA wsaData;\r
9414   int fromPort;\r
9415   char stderrPortStr[MSG_SIZ];\r
9416 \r
9417   /* Initialize socket DLL */\r
9418   wVersionRequested = MAKEWORD(1, 1);\r
9419   err = WSAStartup(wVersionRequested, &wsaData);\r
9420   if (err != 0) return err;\r
9421 \r
9422   /* Resolve remote host name */\r
9423   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9424   if (!(hp = gethostbyname(host))) {\r
9425     unsigned int b0, b1, b2, b3;\r
9426 \r
9427     err = WSAGetLastError();\r
9428 \r
9429     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9430       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9431       hp->h_addrtype = AF_INET;\r
9432       hp->h_length = 4;\r
9433       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9434       hp->h_addr_list[0] = (char *) malloc(4);\r
9435       hp->h_addr_list[0][0] = (char) b0;\r
9436       hp->h_addr_list[0][1] = (char) b1;\r
9437       hp->h_addr_list[0][2] = (char) b2;\r
9438       hp->h_addr_list[0][3] = (char) b3;\r
9439     } else {\r
9440       WSACleanup();\r
9441       return err;\r
9442     }\r
9443   }\r
9444   sa.sin_family = hp->h_addrtype;\r
9445   uport = (unsigned short) 514;\r
9446   sa.sin_port = htons(uport);\r
9447   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9448 \r
9449   /* Bind local socket to unused "privileged" port address\r
9450    */\r
9451   s = INVALID_SOCKET;\r
9452   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9453   mysa.sin_family = AF_INET;\r
9454   mysa.sin_addr.s_addr = INADDR_ANY;\r
9455   for (fromPort = 1023;; fromPort--) {\r
9456     if (fromPort < 0) {\r
9457       WSACleanup();\r
9458       return WSAEADDRINUSE;\r
9459     }\r
9460     if (s == INVALID_SOCKET) {\r
9461       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9462         err = WSAGetLastError();\r
9463         WSACleanup();\r
9464         return err;\r
9465       }\r
9466     }\r
9467     uport = (unsigned short) fromPort;\r
9468     mysa.sin_port = htons(uport);\r
9469     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9470         == SOCKET_ERROR) {\r
9471       err = WSAGetLastError();\r
9472       if (err == WSAEADDRINUSE) continue;\r
9473       WSACleanup();\r
9474       return err;\r
9475     }\r
9476     if (connect(s, (struct sockaddr *) &sa,\r
9477       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9478       err = WSAGetLastError();\r
9479       if (err == WSAEADDRINUSE) {\r
9480         closesocket(s);\r
9481         s = -1;\r
9482         continue;\r
9483       }\r
9484       WSACleanup();\r
9485       return err;\r
9486     }\r
9487     break;\r
9488   }\r
9489 \r
9490   /* Bind stderr local socket to unused "privileged" port address\r
9491    */\r
9492   s2 = INVALID_SOCKET;\r
9493   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9494   mysa.sin_family = AF_INET;\r
9495   mysa.sin_addr.s_addr = INADDR_ANY;\r
9496   for (fromPort = 1023;; fromPort--) {\r
9497     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9498     if (fromPort < 0) {\r
9499       (void) closesocket(s);\r
9500       WSACleanup();\r
9501       return WSAEADDRINUSE;\r
9502     }\r
9503     if (s2 == INVALID_SOCKET) {\r
9504       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9505         err = WSAGetLastError();\r
9506         closesocket(s);\r
9507         WSACleanup();\r
9508         return err;\r
9509       }\r
9510     }\r
9511     uport = (unsigned short) fromPort;\r
9512     mysa.sin_port = htons(uport);\r
9513     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9514         == SOCKET_ERROR) {\r
9515       err = WSAGetLastError();\r
9516       if (err == WSAEADDRINUSE) continue;\r
9517       (void) closesocket(s);\r
9518       WSACleanup();\r
9519       return err;\r
9520     }\r
9521     if (listen(s2, 1) == SOCKET_ERROR) {\r
9522       err = WSAGetLastError();\r
9523       if (err == WSAEADDRINUSE) {\r
9524         closesocket(s2);\r
9525         s2 = INVALID_SOCKET;\r
9526         continue;\r
9527       }\r
9528       (void) closesocket(s);\r
9529       (void) closesocket(s2);\r
9530       WSACleanup();\r
9531       return err;\r
9532     }\r
9533     break;\r
9534   }\r
9535   prevStderrPort = fromPort; // remember port used\r
9536   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9537 \r
9538   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9539     err = WSAGetLastError();\r
9540     (void) closesocket(s);\r
9541     (void) closesocket(s2);\r
9542     WSACleanup();\r
9543     return err;\r
9544   }\r
9545 \r
9546   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9547     err = WSAGetLastError();\r
9548     (void) closesocket(s);\r
9549     (void) closesocket(s2);\r
9550     WSACleanup();\r
9551     return err;\r
9552   }\r
9553   if (*user == NULLCHAR) user = UserName();\r
9554   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9555     err = WSAGetLastError();\r
9556     (void) closesocket(s);\r
9557     (void) closesocket(s2);\r
9558     WSACleanup();\r
9559     return err;\r
9560   }\r
9561   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9562     err = WSAGetLastError();\r
9563     (void) closesocket(s);\r
9564     (void) closesocket(s2);\r
9565     WSACleanup();\r
9566     return err;\r
9567   }\r
9568 \r
9569   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9570     err = WSAGetLastError();\r
9571     (void) closesocket(s);\r
9572     (void) closesocket(s2);\r
9573     WSACleanup();\r
9574     return err;\r
9575   }\r
9576   (void) closesocket(s2);  /* Stop listening */\r
9577 \r
9578   /* Prepare return value */\r
9579   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9580   cp->kind = CPRcmd;\r
9581   cp->sock = s;\r
9582   cp->sock2 = s3;\r
9583   *pr = (ProcRef *) cp;\r
9584 \r
9585   return NO_ERROR;\r
9586 }\r
9587 \r
9588 \r
9589 InputSourceRef\r
9590 AddInputSource(ProcRef pr, int lineByLine,\r
9591                InputCallback func, VOIDSTAR closure)\r
9592 {\r
9593   InputSource *is, *is2 = NULL;\r
9594   ChildProc *cp = (ChildProc *) pr;\r
9595 \r
9596   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9597   is->lineByLine = lineByLine;\r
9598   is->func = func;\r
9599   is->closure = closure;\r
9600   is->second = NULL;\r
9601   is->next = is->buf;\r
9602   if (pr == NoProc) {\r
9603     is->kind = CPReal;\r
9604     consoleInputSource = is;\r
9605   } else {\r
9606     is->kind = cp->kind;\r
9607     /* \r
9608         [AS] Try to avoid a race condition if the thread is given control too early:\r
9609         we create all threads suspended so that the is->hThread variable can be\r
9610         safely assigned, then let the threads start with ResumeThread.\r
9611     */\r
9612     switch (cp->kind) {\r
9613     case CPReal:\r
9614       is->hFile = cp->hFrom;\r
9615       cp->hFrom = NULL; /* now owned by InputThread */\r
9616       is->hThread =\r
9617         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9618                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9619       break;\r
9620 \r
9621     case CPComm:\r
9622       is->hFile = cp->hFrom;\r
9623       cp->hFrom = NULL; /* now owned by InputThread */\r
9624       is->hThread =\r
9625         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9626                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9627       break;\r
9628 \r
9629     case CPSock:\r
9630       is->sock = cp->sock;\r
9631       is->hThread =\r
9632         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9633                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9634       break;\r
9635 \r
9636     case CPRcmd:\r
9637       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9638       *is2 = *is;\r
9639       is->sock = cp->sock;\r
9640       is->second = is2;\r
9641       is2->sock = cp->sock2;\r
9642       is2->second = is2;\r
9643       is->hThread =\r
9644         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9645                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9646       is2->hThread =\r
9647         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9648                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9649       break;\r
9650     }\r
9651 \r
9652     if( is->hThread != NULL ) {\r
9653         ResumeThread( is->hThread );\r
9654     }\r
9655 \r
9656     if( is2 != NULL && is2->hThread != NULL ) {\r
9657         ResumeThread( is2->hThread );\r
9658     }\r
9659   }\r
9660 \r
9661   return (InputSourceRef) is;\r
9662 }\r
9663 \r
9664 void\r
9665 RemoveInputSource(InputSourceRef isr)\r
9666 {\r
9667   InputSource *is;\r
9668 \r
9669   is = (InputSource *) isr;\r
9670   is->hThread = NULL;  /* tell thread to stop */\r
9671   CloseHandle(is->hThread);\r
9672   if (is->second != NULL) {\r
9673     is->second->hThread = NULL;\r
9674     CloseHandle(is->second->hThread);\r
9675   }\r
9676 }\r
9677 \r
9678 int no_wrap(char *message, int count)\r
9679 {\r
9680     ConsoleOutput(message, count, FALSE);\r
9681     return count;\r
9682 }\r
9683 \r
9684 int\r
9685 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9686 {\r
9687   DWORD dOutCount;\r
9688   int outCount = SOCKET_ERROR;\r
9689   ChildProc *cp = (ChildProc *) pr;\r
9690   static OVERLAPPED ovl;\r
9691   static int line = 0;\r
9692 \r
9693   if (pr == NoProc)\r
9694   {\r
9695     if (appData.noJoin || !appData.useInternalWrap)\r
9696       return no_wrap(message, count);\r
9697     else\r
9698     {\r
9699       int width = get_term_width();\r
9700       int len = wrap(NULL, message, count, width, &line);\r
9701       char *msg = malloc(len);\r
9702       int dbgchk;\r
9703 \r
9704       if (!msg)\r
9705         return no_wrap(message, count);\r
9706       else\r
9707       {\r
9708         dbgchk = wrap(msg, message, count, width, &line);\r
9709         if (dbgchk != len && appData.debugMode)\r
9710             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9711         ConsoleOutput(msg, len, FALSE);\r
9712         free(msg);\r
9713         return len;\r
9714       }\r
9715     }\r
9716   }\r
9717 \r
9718   if (ovl.hEvent == NULL) {\r
9719     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9720   }\r
9721   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9722 \r
9723   switch (cp->kind) {\r
9724   case CPSock:\r
9725   case CPRcmd:\r
9726     outCount = send(cp->sock, message, count, 0);\r
9727     if (outCount == SOCKET_ERROR) {\r
9728       *outError = WSAGetLastError();\r
9729     } else {\r
9730       *outError = NO_ERROR;\r
9731     }\r
9732     break;\r
9733 \r
9734   case CPReal:\r
9735     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9736                   &dOutCount, NULL)) {\r
9737       *outError = NO_ERROR;\r
9738       outCount = (int) dOutCount;\r
9739     } else {\r
9740       *outError = GetLastError();\r
9741     }\r
9742     break;\r
9743 \r
9744   case CPComm:\r
9745     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9746                             &dOutCount, &ovl);\r
9747     if (*outError == NO_ERROR) {\r
9748       outCount = (int) dOutCount;\r
9749     }\r
9750     break;\r
9751   }\r
9752   return outCount;\r
9753 }\r
9754 \r
9755 void\r
9756 DoSleep(int n)\r
9757 {\r
9758     if(n != 0) Sleep(n);\r
9759 }\r
9760 \r
9761 int\r
9762 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9763                        long msdelay)\r
9764 {\r
9765   /* Ignore delay, not implemented for WinBoard */\r
9766   return OutputToProcess(pr, message, count, outError);\r
9767 }\r
9768 \r
9769 \r
9770 void\r
9771 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9772                         char *buf, int count, int error)\r
9773 {\r
9774   DisplayFatalError(_("Not implemented"), 0, 1);\r
9775 }\r
9776 \r
9777 /* see wgamelist.c for Game List functions */\r
9778 /* see wedittags.c for Edit Tags functions */\r
9779 \r
9780 \r
9781 int\r
9782 ICSInitScript()\r
9783 {\r
9784   FILE *f;\r
9785   char buf[MSG_SIZ];\r
9786   char *dummy;\r
9787 \r
9788   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9789     f = fopen(buf, "r");\r
9790     if (f != NULL) {\r
9791       ProcessICSInitScript(f);\r
9792       fclose(f);\r
9793       return TRUE;\r
9794     }\r
9795   }\r
9796   return FALSE;\r
9797 }\r
9798 \r
9799 \r
9800 VOID\r
9801 StartAnalysisClock()\r
9802 {\r
9803   if (analysisTimerEvent) return;\r
9804   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9805                                         (UINT) 2000, NULL);\r
9806 }\r
9807 \r
9808 VOID\r
9809 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9810 {\r
9811   highlightInfo.sq[0].x = fromX;\r
9812   highlightInfo.sq[0].y = fromY;\r
9813   highlightInfo.sq[1].x = toX;\r
9814   highlightInfo.sq[1].y = toY;\r
9815 }\r
9816 \r
9817 VOID\r
9818 ClearHighlights()\r
9819 {\r
9820   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9821     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9822 }\r
9823 \r
9824 VOID\r
9825 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9826 {\r
9827   premoveHighlightInfo.sq[0].x = fromX;\r
9828   premoveHighlightInfo.sq[0].y = fromY;\r
9829   premoveHighlightInfo.sq[1].x = toX;\r
9830   premoveHighlightInfo.sq[1].y = toY;\r
9831 }\r
9832 \r
9833 VOID\r
9834 ClearPremoveHighlights()\r
9835 {\r
9836   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9837     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9838 }\r
9839 \r
9840 VOID\r
9841 ShutDownFrontEnd()\r
9842 {\r
9843   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9844   DeleteClipboardTempFiles();\r
9845 }\r
9846 \r
9847 void\r
9848 BoardToTop()\r
9849 {\r
9850     if (IsIconic(hwndMain))\r
9851       ShowWindow(hwndMain, SW_RESTORE);\r
9852 \r
9853     SetActiveWindow(hwndMain);\r
9854 }\r
9855 \r
9856 /*\r
9857  * Prototypes for animation support routines\r
9858  */\r
9859 static void ScreenSquare(int column, int row, POINT * pt);\r
9860 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9861      POINT frames[], int * nFrames);\r
9862 \r
9863 \r
9864 #define kFactor 4\r
9865 \r
9866 void\r
9867 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9868 {       // [HGM] atomic: animate blast wave\r
9869         int i;\r
9870 \r
9871         explodeInfo.fromX = fromX;\r
9872         explodeInfo.fromY = fromY;\r
9873         explodeInfo.toX = toX;\r
9874         explodeInfo.toY = toY;\r
9875         for(i=1; i<4*kFactor; i++) {\r
9876             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9877             DrawPosition(FALSE, board);\r
9878             Sleep(appData.animSpeed);\r
9879         }\r
9880         explodeInfo.radius = 0;\r
9881         DrawPosition(TRUE, board);\r
9882 }\r
9883 \r
9884 void\r
9885 AnimateMove(board, fromX, fromY, toX, toY)\r
9886      Board board;\r
9887      int fromX;\r
9888      int fromY;\r
9889      int toX;\r
9890      int toY;\r
9891 {\r
9892   ChessSquare piece;\r
9893   POINT start, finish, mid;\r
9894   POINT frames[kFactor * 2 + 1];\r
9895   int nFrames, n;\r
9896 \r
9897   if (!appData.animate) return;\r
9898   if (doingSizing) return;\r
9899   if (fromY < 0 || fromX < 0) return;\r
9900   piece = board[fromY][fromX];\r
9901   if (piece >= EmptySquare) return;\r
9902 \r
9903   ScreenSquare(fromX, fromY, &start);\r
9904   ScreenSquare(toX, toY, &finish);\r
9905 \r
9906   /* All moves except knight jumps move in straight line */\r
9907   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9908     mid.x = start.x + (finish.x - start.x) / 2;\r
9909     mid.y = start.y + (finish.y - start.y) / 2;\r
9910   } else {\r
9911     /* Knight: make straight movement then diagonal */\r
9912     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9913        mid.x = start.x + (finish.x - start.x) / 2;\r
9914        mid.y = start.y;\r
9915      } else {\r
9916        mid.x = start.x;\r
9917        mid.y = start.y + (finish.y - start.y) / 2;\r
9918      }\r
9919   }\r
9920   \r
9921   /* Don't use as many frames for very short moves */\r
9922   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9923     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9924   else\r
9925     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9926 \r
9927   animInfo.from.x = fromX;\r
9928   animInfo.from.y = fromY;\r
9929   animInfo.to.x = toX;\r
9930   animInfo.to.y = toY;\r
9931   animInfo.lastpos = start;\r
9932   animInfo.piece = piece;\r
9933   for (n = 0; n < nFrames; n++) {\r
9934     animInfo.pos = frames[n];\r
9935     DrawPosition(FALSE, NULL);\r
9936     animInfo.lastpos = animInfo.pos;\r
9937     Sleep(appData.animSpeed);\r
9938   }\r
9939   animInfo.pos = finish;\r
9940   DrawPosition(FALSE, NULL);\r
9941   animInfo.piece = EmptySquare;\r
9942   Explode(board, fromX, fromY, toX, toY);\r
9943 }\r
9944 \r
9945 /*      Convert board position to corner of screen rect and color       */\r
9946 \r
9947 static void\r
9948 ScreenSquare(column, row, pt)\r
9949      int column; int row; POINT * pt;\r
9950 {\r
9951   if (flipView) {\r
9952     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9953     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9954   } else {\r
9955     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9956     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9957   }\r
9958 }\r
9959 \r
9960 /*      Generate a series of frame coords from start->mid->finish.\r
9961         The movement rate doubles until the half way point is\r
9962         reached, then halves back down to the final destination,\r
9963         which gives a nice slow in/out effect. The algorithmn\r
9964         may seem to generate too many intermediates for short\r
9965         moves, but remember that the purpose is to attract the\r
9966         viewers attention to the piece about to be moved and\r
9967         then to where it ends up. Too few frames would be less\r
9968         noticeable.                                             */\r
9969 \r
9970 static void\r
9971 Tween(start, mid, finish, factor, frames, nFrames)\r
9972      POINT * start; POINT * mid;\r
9973      POINT * finish; int factor;\r
9974      POINT frames[]; int * nFrames;\r
9975 {\r
9976   int n, fraction = 1, count = 0;\r
9977 \r
9978   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9979   for (n = 0; n < factor; n++)\r
9980     fraction *= 2;\r
9981   for (n = 0; n < factor; n++) {\r
9982     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9983     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9984     count ++;\r
9985     fraction = fraction / 2;\r
9986   }\r
9987   \r
9988   /* Midpoint */\r
9989   frames[count] = *mid;\r
9990   count ++;\r
9991   \r
9992   /* Slow out, stepping 1/2, then 1/4, ... */\r
9993   fraction = 2;\r
9994   for (n = 0; n < factor; n++) {\r
9995     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9996     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9997     count ++;\r
9998     fraction = fraction * 2;\r
9999   }\r
10000   *nFrames = count;\r
10001 }\r
10002 \r
10003 void\r
10004 SettingsPopUp(ChessProgramState *cps)\r
10005 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10006       EngineOptionsPopup(savedHwnd, cps);\r
10007 }\r
10008 \r
10009 int flock(int fid, int code)\r
10010 {\r
10011     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10012     OVERLAPPED ov;\r
10013     ov.hEvent = NULL;\r
10014     ov.Offset = 0;\r
10015     ov.OffsetHigh = 0;\r
10016     switch(code) {\r
10017       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10018       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10019       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10020       default: return -1;\r
10021     }\r
10022     return 0;\r
10023 }\r
10024 \r
10025 char *\r
10026 Col2Text (int n)\r
10027 {\r
10028     static int i=0;\r
10029     static char col[8][20];\r
10030     COLORREF color = *(COLORREF *) colorVariable[n];\r
10031     i = i+1 & 7;\r
10032     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10033     return col[i];\r
10034 }\r
10035 \r
10036 void\r
10037 ActivateTheme (int new)\r
10038 {   // Redo initialization of features depending on options that can occur in themes\r
10039    InitTextures();\r
10040    if(new) InitDrawingColors();\r
10041    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10042    InitDrawingSizes(boardSize, 0);\r
10043    InvalidateRect(hwndMain, NULL, TRUE);\r
10044 }\r