1c41752fdb4f59b4bb47ebbf55a06378190f3ecc
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern enum ICS_TYPE ics_type;\r
104 \r
105 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P((char *s));\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129   ChessSquare piece;\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
142 \r
143 typedef struct { // [HGM] atomic\r
144   int fromX, fromY, toX, toY, radius;\r
145 } ExplodeInfo;\r
146 \r
147 static ExplodeInfo explodeInfo;\r
148 \r
149 /* Window class names */\r
150 char szAppName[] = "WinBoard";\r
151 char szConsoleName[] = "WBConsole";\r
152 \r
153 /* Title bar text */\r
154 char szTitle[] = "WinBoard";\r
155 char szConsoleTitle[] = "I C S Interaction";\r
156 \r
157 char *programName;\r
158 char *settingsFileName;\r
159 Boolean saveSettingsOnExit;\r
160 char installDir[MSG_SIZ];\r
161 int errorExitStatus;\r
162 \r
163 BoardSize boardSize;\r
164 Boolean chessProgram;\r
165 //static int boardX, boardY;\r
166 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
167 int squareSize, lineGap, minorSize, border;\r
168 static int winW, winH;\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
170 static int logoHeight = 0;\r
171 static char messageText[MESSAGE_TEXT_MAX];\r
172 static int clockTimerEvent = 0;\r
173 static int loadGameTimerEvent = 0;\r
174 static int analysisTimerEvent = 0;\r
175 static DelayedEventCallback delayedTimerCallback;\r
176 static int delayedTimerEvent = 0;\r
177 static int buttonCount = 2;\r
178 char *icsTextMenuString;\r
179 char *icsNames;\r
180 char *firstChessProgramNames;\r
181 char *secondChessProgramNames;\r
182 \r
183 #define PALETTESIZE 256\r
184 \r
185 HINSTANCE hInst;          /* current instance */\r
186 Boolean alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush,      /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #if defined(_winmajor)\r
226 #define oldDialog (_winmajor < 4)\r
227 #else\r
228 #define oldDialog 0\r
229 #endif\r
230 #endif\r
231 \r
232 #define INTERNATIONAL\r
233 \r
234 #ifdef INTERNATIONAL\r
235 #  define _(s) T_(s)\r
236 #  define N_(s) s\r
237 #else\r
238 #  define _(s) s\r
239 #  define N_(s) s\r
240 #  define T_(s) s\r
241 #  define Translate(x, y)\r
242 #  define LoadLanguageFile(s)\r
243 #endif\r
244 \r
245 #ifdef INTERNATIONAL\r
246 \r
247 Boolean barbaric; // flag indicating if translation is needed\r
248 \r
249 // list of item numbers used in each dialog (used to alter language at run time)\r
250 \r
251 #define ABOUTBOX -1  /* not sure why these are needed */\r
252 #define ABOUTBOX2 -1\r
253 \r
254 int dialogItems[][42] = {\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
257   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
259   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
261   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
264   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
267   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
268 { ABOUTBOX2, IDC_ChessBoard }, \r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
270   OPT_GameListClose, IDC_GameListDoFilter }, \r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
272 { DLG_Error, IDOK }, \r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
274   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
277   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
278   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
279 { DLG_IndexNumber, IDC_Index }, \r
280 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
281 { DLG_TypeInName, IDOK, IDCANCEL }, \r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
283   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
285   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
286   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
287   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
288   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
289   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
290   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
292   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
293   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
294   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
295   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
296   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
297   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
298   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
299   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
301   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
302   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
303   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
304   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
305   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
306   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
307   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
309   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
310   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
311   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
312   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
313   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
314   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
315   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
316   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
318   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
319   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
320   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
321   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
324   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
325 { DLG_MoveHistory }, \r
326 { DLG_EvalGraph }, \r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
330   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
331   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
332   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
334   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
335   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
336 { 0 }\r
337 };\r
338 \r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
340 static int lastChecked;\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
342 extern int tinyLayout;\r
343 extern char * menuBarText[][10];\r
344 \r
345 void\r
346 LoadLanguageFile(char *name)\r
347 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
348     FILE *f;\r
349     int i=0, j=0, n=0, k;\r
350     char buf[MSG_SIZ];\r
351 \r
352     if(!name || name[0] == NULLCHAR) return;\r
353       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
354     appData.language = oldLanguage;\r
355     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
356     if((f = fopen(buf, "r")) == NULL) return;\r
357     while((k = fgetc(f)) != EOF) {\r
358         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
359         languageBuf[i] = k;\r
360         if(k == '\n') {\r
361             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
362                 char *p;\r
363                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
364                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
365                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
366                         english[j] = languageBuf + n + 1; *p = 0;\r
367                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
369                     }\r
370                 }\r
371             }\r
372             n = i + 1;\r
373         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
374             switch(k) {\r
375               case 'n': k = '\n'; break;\r
376               case 'r': k = '\r'; break;\r
377               case 't': k = '\t'; break;\r
378             }\r
379             languageBuf[--i] = k;\r
380         }\r
381         i++;\r
382     }\r
383     fclose(f);\r
384     barbaric = (j != 0);\r
385     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
386 }\r
387 \r
388 char *\r
389 T_(char *s)\r
390 {   // return the translation of the given string\r
391     // efficiency can be improved a lot...\r
392     int i=0;\r
393     static char buf[MSG_SIZ];\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
395     if(!barbaric) return s;\r
396     if(!s) return ""; // sanity\r
397     while(english[i]) {\r
398         if(!strcmp(s, english[i])) return foreign[i];\r
399         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
400             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
401             return buf;\r
402         }\r
403         i++;\r
404     }\r
405     return s;\r
406 }\r
407 \r
408 void\r
409 Translate(HWND hDlg, int dialogID)\r
410 {   // translate all text items in the given dialog\r
411     int i=0, j, k;\r
412     char buf[MSG_SIZ], *s;\r
413     if(!barbaric) return;\r
414     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
415     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
416     GetWindowText( hDlg, buf, MSG_SIZ );\r
417     s = T_(buf);\r
418     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
419     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
420         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
421         if(strlen(buf) == 0) continue;\r
422         s = T_(buf);\r
423         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
424     }\r
425 }\r
426 \r
427 HMENU\r
428 TranslateOneMenu(int i, HMENU subMenu)\r
429 {\r
430     int j;\r
431     static MENUITEMINFO info;\r
432 \r
433     info.cbSize = sizeof(MENUITEMINFO);\r
434     info.fMask = MIIM_STATE | MIIM_TYPE;\r
435           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
436             char buf[MSG_SIZ];\r
437             info.dwTypeData = buf;\r
438             info.cch = sizeof(buf);\r
439             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
440             if(i < 10) {\r
441                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
442                 else menuText[i][j] = strdup(buf); // remember original on first change\r
443             }\r
444             if(buf[0] == NULLCHAR) continue;\r
445             info.dwTypeData = T_(buf);\r
446             info.cch = strlen(buf)+1;\r
447             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
448           }\r
449     return subMenu;\r
450 }\r
451 \r
452 void\r
453 TranslateMenus(int addLanguage)\r
454 {\r
455     int i;\r
456     WIN32_FIND_DATA fileData;\r
457     HANDLE hFind;\r
458 #define IDM_English 1970\r
459     if(1) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
462           HMENU subMenu = GetSubMenu(mainMenu, i);\r
463           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
464                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
465           TranslateOneMenu(i, subMenu);\r
466         }\r
467         DrawMenuBar(hwndMain);\r
468     }\r
469 \r
470     if(!addLanguage) return;\r
471     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
472         HMENU mainMenu = GetMenu(hwndMain);\r
473         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
474         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
475         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
476         i = 0; lastChecked = IDM_English;\r
477         do {\r
478             char *p, *q = fileData.cFileName;\r
479             int checkFlag = MF_UNCHECKED;\r
480             languageFile[i] = strdup(q);\r
481             if(barbaric && !strcmp(oldLanguage, q)) {\r
482                 checkFlag = MF_CHECKED;\r
483                 lastChecked = IDM_English + i + 1;\r
484                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
485             }\r
486             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
487             p = strstr(fileData.cFileName, ".lng");\r
488             if(p) *p = 0;\r
489             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
490         } while(FindNextFile(hFind, &fileData));\r
491         FindClose(hFind);\r
492     }\r
493 }\r
494 \r
495 #endif\r
496 \r
497 #define IDM_RecentEngines 3000\r
498 \r
499 void\r
500 RecentEngineMenu (char *s)\r
501 {\r
502     if(appData.icsActive) return;\r
503     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
504         HMENU mainMenu = GetMenu(hwndMain);\r
505         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
506         int i=IDM_RecentEngines;\r
507         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
508         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
509         while(*s) {\r
510           char *p = strchr(s, '\n');\r
511           if(p == NULL) return; // malformed!\r
512           *p = NULLCHAR;\r
513           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
514           *p = '\n';\r
515           s = p+1;\r
516         }\r
517     }\r
518 }\r
519 \r
520 \r
521 typedef struct {\r
522   char *name;\r
523   int squareSize;\r
524   int lineGap;\r
525   int smallLayout;\r
526   int tinyLayout;\r
527   int cliWidth, cliHeight;\r
528 } SizeInfo;\r
529 \r
530 SizeInfo sizeInfo[] = \r
531 {\r
532   { "tiny",     21, 0, 1, 1, 0, 0 },\r
533   { "teeny",    25, 1, 1, 1, 0, 0 },\r
534   { "dinky",    29, 1, 1, 1, 0, 0 },\r
535   { "petite",   33, 1, 1, 1, 0, 0 },\r
536   { "slim",     37, 2, 1, 0, 0, 0 },\r
537   { "small",    40, 2, 1, 0, 0, 0 },\r
538   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
539   { "middling", 49, 2, 0, 0, 0, 0 },\r
540   { "average",  54, 2, 0, 0, 0, 0 },\r
541   { "moderate", 58, 3, 0, 0, 0, 0 },\r
542   { "medium",   64, 3, 0, 0, 0, 0 },\r
543   { "bulky",    72, 3, 0, 0, 0, 0 },\r
544   { "large",    80, 3, 0, 0, 0, 0 },\r
545   { "big",      87, 3, 0, 0, 0, 0 },\r
546   { "huge",     95, 3, 0, 0, 0, 0 },\r
547   { "giant",    108, 3, 0, 0, 0, 0 },\r
548   { "colossal", 116, 4, 0, 0, 0, 0 },\r
549   { "titanic",  129, 4, 0, 0, 0, 0 },\r
550   { NULL, 0, 0, 0, 0, 0, 0 }\r
551 };\r
552 \r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
555 {\r
556   { 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
557   { 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
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574 };\r
575 \r
576 MyFont *font[NUM_SIZES][NUM_FONTS];\r
577 \r
578 typedef struct {\r
579   char *label;\r
580   int id;\r
581   HWND hwnd;\r
582   WNDPROC wndproc;\r
583 } MyButtonDesc;\r
584 \r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
586 #define N_BUTTONS 5\r
587 \r
588 MyButtonDesc buttonDesc[N_BUTTONS] =\r
589 {\r
590   {"<<", IDM_ToStart, NULL, NULL},\r
591   {"<", IDM_Backward, NULL, NULL},\r
592   {"P", IDM_Pause, NULL, NULL},\r
593   {">", IDM_Forward, NULL, NULL},\r
594   {">>", IDM_ToEnd, NULL, NULL},\r
595 };\r
596 \r
597 int tinyLayout = 0, smallLayout = 0;\r
598 #define MENU_BAR_ITEMS 9\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
600   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
601   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
602 };\r
603 \r
604 \r
605 MySound sounds[(int)NSoundClasses];\r
606 MyTextAttribs textAttribs[(int)NColorClasses];\r
607 \r
608 MyColorizeAttribs colorizeAttribs[] = {\r
609   { (COLORREF)0, 0, N_("Shout Text") },\r
610   { (COLORREF)0, 0, N_("SShout/CShout") },\r
611   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
612   { (COLORREF)0, 0, N_("Channel Text") },\r
613   { (COLORREF)0, 0, N_("Kibitz Text") },\r
614   { (COLORREF)0, 0, N_("Tell Text") },\r
615   { (COLORREF)0, 0, N_("Challenge Text") },\r
616   { (COLORREF)0, 0, N_("Request Text") },\r
617   { (COLORREF)0, 0, N_("Seek Text") },\r
618   { (COLORREF)0, 0, N_("Normal Text") },\r
619   { (COLORREF)0, 0, N_("None") }\r
620 };\r
621 \r
622 \r
623 \r
624 static char *commentTitle;\r
625 static char *commentText;\r
626 static int commentIndex;\r
627 static Boolean editComment = FALSE;\r
628 \r
629 \r
630 char errorTitle[MSG_SIZ];\r
631 char errorMessage[2*MSG_SIZ];\r
632 HWND errorDialog = NULL;\r
633 BOOLEAN moveErrorMessageUp = FALSE;\r
634 BOOLEAN consoleEcho = TRUE;\r
635 CHARFORMAT consoleCF;\r
636 COLORREF consoleBackgroundColor;\r
637 \r
638 char *programVersion;\r
639 \r
640 #define CPReal 1\r
641 #define CPComm 2\r
642 #define CPSock 3\r
643 #define CPRcmd 4\r
644 typedef int CPKind;\r
645 \r
646 typedef struct {\r
647   CPKind kind;\r
648   HANDLE hProcess;\r
649   DWORD pid;\r
650   HANDLE hTo;\r
651   HANDLE hFrom;\r
652   SOCKET sock;\r
653   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
654 } ChildProc;\r
655 \r
656 #define INPUT_SOURCE_BUF_SIZE 4096\r
657 \r
658 typedef struct _InputSource {\r
659   CPKind kind;\r
660   HANDLE hFile;\r
661   SOCKET sock;\r
662   int lineByLine;\r
663   HANDLE hThread;\r
664   DWORD id;\r
665   char buf[INPUT_SOURCE_BUF_SIZE];\r
666   char *next;\r
667   DWORD count;\r
668   int error;\r
669   InputCallback func;\r
670   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
671   VOIDSTAR closure;\r
672 } InputSource;\r
673 \r
674 InputSource *consoleInputSource;\r
675 \r
676 DCB dcb;\r
677 \r
678 /* forward */\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
680 VOID ConsoleCreate();\r
681 LRESULT CALLBACK\r
682   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);\r
686 LRESULT CALLBACK\r
687   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
689 void ParseIcsTextMenu(char *icsTextMenuString);\r
690 VOID PopUpNameDialog(char firstchar);\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
692 \r
693 /* [AS] */\r
694 int NewGameFRC();\r
695 int GameListOptions();\r
696 \r
697 int dummy; // [HGM] for obsolete args\r
698 \r
699 HWND hwndMain = NULL;        /* root window*/\r
700 HWND hwndConsole = NULL;\r
701 HWND commentDialog = NULL;\r
702 HWND moveHistoryDialog = NULL;\r
703 HWND evalGraphDialog = NULL;\r
704 HWND engineOutputDialog = NULL;\r
705 HWND gameListDialog = NULL;\r
706 HWND editTagsDialog = NULL;\r
707 \r
708 int commentUp = FALSE;\r
709 \r
710 WindowPlacement wpMain;\r
711 WindowPlacement wpConsole;\r
712 WindowPlacement wpComment;\r
713 WindowPlacement wpMoveHistory;\r
714 WindowPlacement wpEvalGraph;\r
715 WindowPlacement wpEngineOutput;\r
716 WindowPlacement wpGameList;\r
717 WindowPlacement wpTags;\r
718 \r
719 VOID EngineOptionsPopup(); // [HGM] settings\r
720 \r
721 VOID GothicPopUp(char *title, VariantClass variant);\r
722 /*\r
723  * Setting "frozen" should disable all user input other than deleting\r
724  * the window.  We do this while engines are initializing themselves.\r
725  */\r
726 static int frozen = 0;\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];\r
728 void FreezeUI()\r
729 {\r
730   HMENU hmenu;\r
731   int i;\r
732 \r
733   if (frozen) return;\r
734   frozen = 1;\r
735   hmenu = GetMenu(hwndMain);\r
736   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
737     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
738   }\r
739   DrawMenuBar(hwndMain);\r
740 }\r
741 \r
742 /* Undo a FreezeUI */\r
743 void ThawUI()\r
744 {\r
745   HMENU hmenu;\r
746   int i;\r
747 \r
748   if (!frozen) return;\r
749   frozen = 0;\r
750   hmenu = GetMenu(hwndMain);\r
751   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
752     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
753   }\r
754   DrawMenuBar(hwndMain);\r
755 }\r
756 \r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
758 \r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
760 #ifdef JAWS\r
761 #include "jaws.c"\r
762 #else\r
763 #define JAWS_INIT\r
764 #define JAWS_ARGS\r
765 #define JAWS_ALT_INTERCEPT\r
766 #define JAWS_KBUP_NAVIGATION\r
767 #define JAWS_KBDOWN_NAVIGATION\r
768 #define JAWS_MENU_ITEMS\r
769 #define JAWS_SILENCE\r
770 #define JAWS_REPLAY\r
771 #define JAWS_ACCEL\r
772 #define JAWS_COPYRIGHT\r
773 #define JAWS_DELETE(X) X\r
774 #define SAYMACHINEMOVE()\r
775 #define SAY(X)\r
776 #endif\r
777 \r
778 /*---------------------------------------------------------------------------*\\r
779  *\r
780  * WinMain\r
781  *\r
782 \*---------------------------------------------------------------------------*/\r
783 \r
784 int APIENTRY\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
786         LPSTR lpCmdLine, int nCmdShow)\r
787 {\r
788   MSG msg;\r
789   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
790 //  INITCOMMONCONTROLSEX ex;\r
791 \r
792   debugFP = stderr;\r
793 \r
794   LoadLibrary("RICHED32.DLL");\r
795   consoleCF.cbSize = sizeof(CHARFORMAT);\r
796 \r
797   if (!InitApplication(hInstance)) {\r
798     return (FALSE);\r
799   }\r
800   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
801     return (FALSE);\r
802   }\r
803 \r
804   JAWS_INIT\r
805   TranslateMenus(1);\r
806 \r
807 //  InitCommonControlsEx(&ex);\r
808   InitCommonControls();\r
809 \r
810   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
811   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
812   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
813 \r
814   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
815 \r
816   while (GetMessage(&msg, /* message structure */\r
817                     NULL, /* handle of window receiving the message */\r
818                     0,    /* lowest message to examine */\r
819                     0))   /* highest message to examine */\r
820     {\r
821 \r
822       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
823         // [HGM] navigate: switch between all windows with tab\r
824         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
825         int i, currentElement = 0;\r
826 \r
827         // first determine what element of the chain we come from (if any)\r
828         if(appData.icsActive) {\r
829             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
830             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
831         }\r
832         if(engineOutputDialog && EngineOutputIsUp()) {\r
833             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
834             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
835         }\r
836         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
837             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
838         }\r
839         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
840         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
841         if(msg.hwnd == e1)                 currentElement = 2; else\r
842         if(msg.hwnd == e2)                 currentElement = 3; else\r
843         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
844         if(msg.hwnd == mh)                currentElement = 4; else\r
845         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
846         if(msg.hwnd == hText)  currentElement = 5; else\r
847         if(msg.hwnd == hInput) currentElement = 6; else\r
848         for (i = 0; i < N_BUTTONS; i++) {\r
849             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
850         }\r
851 \r
852         // determine where to go to\r
853         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
854           do {\r
855             currentElement = (currentElement + direction) % 7;\r
856             switch(currentElement) {\r
857                 case 0:\r
858                   h = hwndMain; break; // passing this case always makes the loop exit\r
859                 case 1:\r
860                   h = buttonDesc[0].hwnd; break; // could be NULL\r
861                 case 2:\r
862                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
863                   h = e1; break;\r
864                 case 3:\r
865                   if(!EngineOutputIsUp()) continue;\r
866                   h = e2; break;\r
867                 case 4:\r
868                   if(!MoveHistoryIsUp()) continue;\r
869                   h = mh; break;\r
870 //              case 6: // input to eval graph does not seem to get here!\r
871 //                if(!EvalGraphIsUp()) continue;\r
872 //                h = evalGraphDialog; break;\r
873                 case 5:\r
874                   if(!appData.icsActive) continue;\r
875                   SAY("display");\r
876                   h = hText; break;\r
877                 case 6:\r
878                   if(!appData.icsActive) continue;\r
879                   SAY("input");\r
880                   h = hInput; break;\r
881             }\r
882           } while(h == 0);\r
883 \r
884           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
885           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
886           SetFocus(h);\r
887 \r
888           continue; // this message now has been processed\r
889         }\r
890       }\r
891 \r
892       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
893           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
894           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
895           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
896           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
897           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
898           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
899           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
900           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
901           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
902         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
903         for(i=0; i<MAX_CHAT; i++) \r
904             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
905                 done = 1; break;\r
906         }\r
907         if(done) continue; // [HGM] chat: end patch\r
908         TranslateMessage(&msg); /* Translates virtual key codes */\r
909         DispatchMessage(&msg);  /* Dispatches message to window */\r
910       }\r
911     }\r
912 \r
913 \r
914   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
915 }\r
916 \r
917 /*---------------------------------------------------------------------------*\\r
918  *\r
919  * Initialization functions\r
920  *\r
921 \*---------------------------------------------------------------------------*/\r
922 \r
923 void\r
924 SetUserLogo()\r
925 {   // update user logo if necessary\r
926     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
927 \r
928     if(appData.autoLogo) {\r
929           curName = UserName();\r
930           if(strcmp(curName, oldUserName)) {\r
931                 GetCurrentDirectory(MSG_SIZ, dir);\r
932                 SetCurrentDirectory(installDir);\r
933                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
934                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
935                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
936                 if(userLogo == NULL)\r
937                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
938                 SetCurrentDirectory(dir); /* return to prev directory */\r
939           }\r
940     }\r
941 }\r
942 \r
943 BOOL\r
944 InitApplication(HINSTANCE hInstance)\r
945 {\r
946   WNDCLASS wc;\r
947 \r
948   /* Fill in window class structure with parameters that describe the */\r
949   /* main window. */\r
950 \r
951   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
952   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
953   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
954   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
955   wc.hInstance     = hInstance;         /* Owner of this class */\r
956   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
957   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
958   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
959   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
960   wc.lpszClassName = szAppName;                 /* Name to register as */\r
961 \r
962   /* Register the window class and return success/failure code. */\r
963   if (!RegisterClass(&wc)) return FALSE;\r
964 \r
965   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
966   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
967   wc.cbClsExtra    = 0;\r
968   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
969   wc.hInstance     = hInstance;\r
970   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
971   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
972   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
973   wc.lpszMenuName  = NULL;\r
974   wc.lpszClassName = szConsoleName;\r
975 \r
976   if (!RegisterClass(&wc)) return FALSE;\r
977   return TRUE;\r
978 }\r
979 \r
980 \r
981 /* Set by InitInstance, used by EnsureOnScreen */\r
982 int screenHeight, screenWidth;\r
983 \r
984 void\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
986 {\r
987 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
988   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
989   if (*x > screenWidth - 32) *x = 0;\r
990   if (*y > screenHeight - 32) *y = 0;\r
991   if (*x < minX) *x = minX;\r
992   if (*y < minY) *y = minY;\r
993 }\r
994 \r
995 VOID\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
997 {\r
998   char buf[MSG_SIZ], dir[MSG_SIZ];\r
999   GetCurrentDirectory(MSG_SIZ, dir);\r
1000   SetCurrentDirectory(installDir);\r
1001   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1002       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1003 \r
1004       if (cps->programLogo == NULL && appData.debugMode) {\r
1005           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1006       }\r
1007   } else if(appData.autoLogo) {\r
1008       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1009         char *opponent = "";\r
1010         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1011         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1012         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1013         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1014             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1015             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1016         }\r
1017       } else\r
1018       if(appData.directory[n] && appData.directory[n][0]) {\r
1019         SetCurrentDirectory(appData.directory[n]);\r
1020         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1021       }\r
1022   }\r
1023   SetCurrentDirectory(dir); /* return to prev directory */\r
1024 }\r
1025 \r
1026 VOID\r
1027 InitTextures()\r
1028 {\r
1029   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1030   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1031   \r
1032   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1033       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1034       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1035       liteBackTextureMode = appData.liteBackTextureMode;\r
1036 \r
1037       if (liteBackTexture == NULL && appData.debugMode) {\r
1038           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1039       }\r
1040   }\r
1041   \r
1042   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1043       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1044       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1045       darkBackTextureMode = appData.darkBackTextureMode;\r
1046 \r
1047       if (darkBackTexture == NULL && appData.debugMode) {\r
1048           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1049       }\r
1050   }\r
1051 }\r
1052 \r
1053 BOOL\r
1054 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1055 {\r
1056   HWND hwnd; /* Main window handle. */\r
1057   int ibs;\r
1058   WINDOWPLACEMENT wp;\r
1059   char *filepart;\r
1060 \r
1061   hInst = hInstance;    /* Store instance handle in our global variable */\r
1062   programName = szAppName;\r
1063 \r
1064   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1065     *filepart = NULLCHAR;\r
1066     SetCurrentDirectory(installDir);\r
1067   } else {\r
1068     GetCurrentDirectory(MSG_SIZ, installDir);\r
1069   }\r
1070   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1071   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1072   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1073   /* xboard, and older WinBoards, controlled the move sound with the\r
1074      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1075      always turn the option on (so that the backend will call us),\r
1076      then let the user turn the sound off by setting it to silence if\r
1077      desired.  To accommodate old winboard.ini files saved by old\r
1078      versions of WinBoard, we also turn off the sound if the option\r
1079      was initially set to false. [HGM] taken out of InitAppData */\r
1080   if (!appData.ringBellAfterMoves) {\r
1081     sounds[(int)SoundMove].name = strdup("");\r
1082     appData.ringBellAfterMoves = TRUE;\r
1083   }\r
1084   if (appData.debugMode) {\r
1085     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1086     setbuf(debugFP, NULL);\r
1087   }\r
1088 \r
1089   LoadLanguageFile(appData.language);\r
1090 \r
1091   InitBackEnd1();\r
1092 \r
1093 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1094 //  InitEngineUCI( installDir, &second );\r
1095 \r
1096   /* Create a main window for this application instance. */\r
1097   hwnd = CreateWindow(szAppName, szTitle,\r
1098                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1099                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1100                       NULL, NULL, hInstance, NULL);\r
1101   hwndMain = hwnd;\r
1102 \r
1103   /* If window could not be created, return "failure" */\r
1104   if (!hwnd) {\r
1105     return (FALSE);\r
1106   }\r
1107 \r
1108   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1109   LoadLogo(&first, 0, FALSE);\r
1110   LoadLogo(&second, 1, appData.icsActive);\r
1111 \r
1112   SetUserLogo();\r
1113 \r
1114   iconWhite = LoadIcon(hInstance, "icon_white");\r
1115   iconBlack = LoadIcon(hInstance, "icon_black");\r
1116   iconCurrent = iconWhite;\r
1117   InitDrawingColors();\r
1118   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1119   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1120   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1121     /* Compute window size for each board size, and use the largest\r
1122        size that fits on this screen as the default. */\r
1123     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1124     if (boardSize == (BoardSize)-1 &&\r
1125         winH <= screenHeight\r
1126            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1127         && winW <= screenWidth) {\r
1128       boardSize = (BoardSize)ibs;\r
1129     }\r
1130   }\r
1131 \r
1132   InitDrawingSizes(boardSize, 0);\r
1133   RecentEngineMenu(appData.recentEngineList);\r
1134   InitMenuChecks();\r
1135   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1136 \r
1137   /* [AS] Load textures if specified */\r
1138   InitTextures();\r
1139 \r
1140   mysrandom( (unsigned) time(NULL) );\r
1141 \r
1142   /* [AS] Restore layout */\r
1143   if( wpMoveHistory.visible ) {\r
1144       MoveHistoryPopUp();\r
1145   }\r
1146 \r
1147   if( wpEvalGraph.visible ) {\r
1148       EvalGraphPopUp();\r
1149   }\r
1150 \r
1151   if( wpEngineOutput.visible ) {\r
1152       EngineOutputPopUp();\r
1153   }\r
1154 \r
1155   /* Make the window visible; update its client area; and return "success" */\r
1156   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1157   wp.length = sizeof(WINDOWPLACEMENT);\r
1158   wp.flags = 0;\r
1159   wp.showCmd = nCmdShow;\r
1160   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1161   wp.rcNormalPosition.left = wpMain.x;\r
1162   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1163   wp.rcNormalPosition.top = wpMain.y;\r
1164   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1165   SetWindowPlacement(hwndMain, &wp);\r
1166 \r
1167   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1168 \r
1169   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1170                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1171 \r
1172   if (hwndConsole) {\r
1173 #if AOT_CONSOLE\r
1174     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1175                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1176 #endif\r
1177     ShowWindow(hwndConsole, nCmdShow);\r
1178     SetActiveWindow(hwndConsole);\r
1179   }\r
1180   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1181   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1182 \r
1183   return TRUE;\r
1184 \r
1185 }\r
1186 \r
1187 VOID\r
1188 InitMenuChecks()\r
1189 {\r
1190   HMENU hmenu = GetMenu(hwndMain);\r
1191 \r
1192   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1193                         MF_BYCOMMAND|((appData.icsActive &&\r
1194                                        *appData.icsCommPort != NULLCHAR) ?\r
1195                                       MF_ENABLED : MF_GRAYED));\r
1196   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1197                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1198                                      MF_CHECKED : MF_UNCHECKED));\r
1199 }\r
1200 \r
1201 //---------------------------------------------------------------------------------------------------------\r
1202 \r
1203 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1204 #define XBOARD FALSE\r
1205 \r
1206 #define OPTCHAR "/"\r
1207 #define SEPCHAR "="\r
1208 #define TOPLEVEL 0\r
1209 \r
1210 #include "args.h"\r
1211 \r
1212 // front-end part of option handling\r
1213 \r
1214 VOID\r
1215 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1216 {\r
1217   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1218   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1219   DeleteDC(hdc);\r
1220   lf->lfWidth = 0;\r
1221   lf->lfEscapement = 0;\r
1222   lf->lfOrientation = 0;\r
1223   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1224   lf->lfItalic = mfp->italic;\r
1225   lf->lfUnderline = mfp->underline;\r
1226   lf->lfStrikeOut = mfp->strikeout;\r
1227   lf->lfCharSet = mfp->charset;\r
1228   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1229   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1230   lf->lfQuality = DEFAULT_QUALITY;\r
1231   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1232     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1233 }\r
1234 \r
1235 void\r
1236 CreateFontInMF(MyFont *mf)\r
1237\r
1238   LFfromMFP(&mf->lf, &mf->mfp);\r
1239   if (mf->hf) DeleteObject(mf->hf);\r
1240   mf->hf = CreateFontIndirect(&mf->lf);\r
1241 }\r
1242 \r
1243 // [HGM] This platform-dependent table provides the location for storing the color info\r
1244 void *\r
1245 colorVariable[] = {\r
1246   &whitePieceColor, \r
1247   &blackPieceColor, \r
1248   &lightSquareColor,\r
1249   &darkSquareColor, \r
1250   &highlightSquareColor,\r
1251   &premoveHighlightColor,\r
1252   NULL,\r
1253   &consoleBackgroundColor,\r
1254   &appData.fontForeColorWhite,\r
1255   &appData.fontBackColorWhite,\r
1256   &appData.fontForeColorBlack,\r
1257   &appData.fontBackColorBlack,\r
1258   &appData.evalHistColorWhite,\r
1259   &appData.evalHistColorBlack,\r
1260   &appData.highlightArrowColor,\r
1261 };\r
1262 \r
1263 /* Command line font name parser.  NULL name means do nothing.\r
1264    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1265    For backward compatibility, syntax without the colon is also\r
1266    accepted, but font names with digits in them won't work in that case.\r
1267 */\r
1268 VOID\r
1269 ParseFontName(char *name, MyFontParams *mfp)\r
1270 {\r
1271   char *p, *q;\r
1272   if (name == NULL) return;\r
1273   p = name;\r
1274   q = strchr(p, ':');\r
1275   if (q) {\r
1276     if (q - p >= sizeof(mfp->faceName))\r
1277       ExitArgError(_("Font name too long:"), name, TRUE);\r
1278     memcpy(mfp->faceName, p, q - p);\r
1279     mfp->faceName[q - p] = NULLCHAR;\r
1280     p = q + 1;\r
1281   } else {\r
1282     q = mfp->faceName;\r
1283 \r
1284     while (*p && !isdigit(*p)) {\r
1285       *q++ = *p++;\r
1286       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1287         ExitArgError(_("Font name too long:"), name, TRUE);\r
1288     }\r
1289     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1290     *q = NULLCHAR;\r
1291   }\r
1292   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1293   mfp->pointSize = (float) atof(p);\r
1294   mfp->bold = (strchr(p, 'b') != NULL);\r
1295   mfp->italic = (strchr(p, 'i') != NULL);\r
1296   mfp->underline = (strchr(p, 'u') != NULL);\r
1297   mfp->strikeout = (strchr(p, 's') != NULL);\r
1298   mfp->charset = DEFAULT_CHARSET;\r
1299   q = strchr(p, 'c');\r
1300   if (q)\r
1301     mfp->charset = (BYTE) atoi(q+1);\r
1302 }\r
1303 \r
1304 void\r
1305 ParseFont(char *name, int number)\r
1306 { // wrapper to shield back-end from 'font'\r
1307   ParseFontName(name, &font[boardSize][number]->mfp);\r
1308 }\r
1309 \r
1310 void\r
1311 SetFontDefaults()\r
1312 { // in WB  we have a 2D array of fonts; this initializes their description\r
1313   int i, j;\r
1314   /* Point font array elements to structures and\r
1315      parse default font names */\r
1316   for (i=0; i<NUM_FONTS; i++) {\r
1317     for (j=0; j<NUM_SIZES; j++) {\r
1318       font[j][i] = &fontRec[j][i];\r
1319       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1320     }\r
1321   }\r
1322 }\r
1323 \r
1324 void\r
1325 CreateFonts()\r
1326 { // here we create the actual fonts from the selected descriptions\r
1327   int i, j;\r
1328   for (i=0; i<NUM_FONTS; i++) {\r
1329     for (j=0; j<NUM_SIZES; j++) {\r
1330       CreateFontInMF(font[j][i]);\r
1331     }\r
1332   }\r
1333 }\r
1334 /* Color name parser.\r
1335    X version accepts X color names, but this one\r
1336    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1337 COLORREF\r
1338 ParseColorName(char *name)\r
1339 {\r
1340   int red, green, blue, count;\r
1341   char buf[MSG_SIZ];\r
1342 \r
1343   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1344   if (count != 3) {\r
1345     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1346       &red, &green, &blue);\r
1347   }\r
1348   if (count != 3) {\r
1349     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1350     DisplayError(buf, 0);\r
1351     return RGB(0, 0, 0);\r
1352   }\r
1353   return PALETTERGB(red, green, blue);\r
1354 }\r
1355 \r
1356 void\r
1357 ParseColor(int n, char *name)\r
1358 { // for WinBoard the color is an int, which needs to be derived from the string\r
1359   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1360 }\r
1361 \r
1362 void\r
1363 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1364 {\r
1365   char *e = argValue;\r
1366   int eff = 0;\r
1367 \r
1368   while (*e) {\r
1369     if (*e == 'b')      eff |= CFE_BOLD;\r
1370     else if (*e == 'i') eff |= CFE_ITALIC;\r
1371     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1372     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1373     else if (*e == '#' || isdigit(*e)) break;\r
1374     e++;\r
1375   }\r
1376   *effects = eff;\r
1377   *color   = ParseColorName(e);\r
1378 }\r
1379 \r
1380 void\r
1381 ParseTextAttribs(ColorClass cc, char *s)\r
1382 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1383     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1384     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1385 }\r
1386 \r
1387 void\r
1388 ParseBoardSize(void *addr, char *name)\r
1389 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1390   BoardSize bs = SizeTiny;\r
1391   while (sizeInfo[bs].name != NULL) {\r
1392     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1393         *(BoardSize *)addr = bs;\r
1394         return;\r
1395     }\r
1396     bs++;\r
1397   }\r
1398   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1399 }\r
1400 \r
1401 void\r
1402 LoadAllSounds()\r
1403 { // [HGM] import name from appData first\r
1404   ColorClass cc;\r
1405   SoundClass sc;\r
1406   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1407     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1408     textAttribs[cc].sound.data = NULL;\r
1409     MyLoadSound(&textAttribs[cc].sound);\r
1410   }\r
1411   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1412     textAttribs[cc].sound.name = strdup("");\r
1413     textAttribs[cc].sound.data = NULL;\r
1414   }\r
1415   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1416     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1417     sounds[sc].data = NULL;\r
1418     MyLoadSound(&sounds[sc]);\r
1419   }\r
1420 }\r
1421 \r
1422 void\r
1423 SetCommPortDefaults()\r
1424 {\r
1425    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1426   dcb.DCBlength = sizeof(DCB);\r
1427   dcb.BaudRate = 9600;\r
1428   dcb.fBinary = TRUE;\r
1429   dcb.fParity = FALSE;\r
1430   dcb.fOutxCtsFlow = FALSE;\r
1431   dcb.fOutxDsrFlow = FALSE;\r
1432   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1433   dcb.fDsrSensitivity = FALSE;\r
1434   dcb.fTXContinueOnXoff = TRUE;\r
1435   dcb.fOutX = FALSE;\r
1436   dcb.fInX = FALSE;\r
1437   dcb.fNull = FALSE;\r
1438   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1439   dcb.fAbortOnError = FALSE;\r
1440   dcb.ByteSize = 7;\r
1441   dcb.Parity = SPACEPARITY;\r
1442   dcb.StopBits = ONESTOPBIT;\r
1443 }\r
1444 \r
1445 // [HGM] args: these three cases taken out to stay in front-end\r
1446 void\r
1447 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1448 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1449         // while the curent board size determines the element. This system should be ported to XBoard.\r
1450         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1451         int bs;\r
1452         for (bs=0; bs<NUM_SIZES; bs++) {\r
1453           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1454           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1455           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1456             ad->argName, mfp->faceName, mfp->pointSize,\r
1457             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1458             mfp->bold ? "b" : "",\r
1459             mfp->italic ? "i" : "",\r
1460             mfp->underline ? "u" : "",\r
1461             mfp->strikeout ? "s" : "",\r
1462             (int)mfp->charset);\r
1463         }\r
1464       }\r
1465 \r
1466 void\r
1467 ExportSounds()\r
1468 { // [HGM] copy the names from the internal WB variables to appData\r
1469   ColorClass cc;\r
1470   SoundClass sc;\r
1471   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1472     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1473   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1474     (&appData.soundMove)[sc] = sounds[sc].name;\r
1475 }\r
1476 \r
1477 void\r
1478 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1479 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1480         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1481         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1482           (ta->effects & CFE_BOLD) ? "b" : "",\r
1483           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1484           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1485           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1486           (ta->effects) ? " " : "",\r
1487           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1488       }\r
1489 \r
1490 void\r
1491 SaveColor(FILE *f, ArgDescriptor *ad)\r
1492 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1493         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1494         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1495           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1496 }\r
1497 \r
1498 void\r
1499 SaveBoardSize(FILE *f, char *name, void *addr)\r
1500 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1501   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1502 }\r
1503 \r
1504 void\r
1505 ParseCommPortSettings(char *s)\r
1506 { // wrapper to keep dcb from back-end\r
1507   ParseCommSettings(s, &dcb);\r
1508 }\r
1509 \r
1510 void\r
1511 GetWindowCoords()\r
1512 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1513   GetActualPlacement(hwndMain, &wpMain);\r
1514   GetActualPlacement(hwndConsole, &wpConsole);\r
1515   GetActualPlacement(commentDialog, &wpComment);\r
1516   GetActualPlacement(editTagsDialog, &wpTags);\r
1517   GetActualPlacement(gameListDialog, &wpGameList);\r
1518   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1519   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1520   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1521 }\r
1522 \r
1523 void\r
1524 PrintCommPortSettings(FILE *f, char *name)\r
1525 { // wrapper to shield back-end from DCB\r
1526       PrintCommSettings(f, name, &dcb);\r
1527 }\r
1528 \r
1529 int\r
1530 MySearchPath(char *installDir, char *name, char *fullname)\r
1531 {\r
1532   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1533   if(name[0]== '%') {\r
1534     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1535     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1536       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1537       *strchr(buf, '%') = 0;\r
1538       strcat(fullname, getenv(buf));\r
1539       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1540     }\r
1541     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1542     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1543     return (int) strlen(fullname);\r
1544   }\r
1545   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1546 }\r
1547 \r
1548 int\r
1549 MyGetFullPathName(char *name, char *fullname)\r
1550 {\r
1551   char *dummy;\r
1552   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1553 }\r
1554 \r
1555 int\r
1556 MainWindowUp()\r
1557 { // [HGM] args: allows testing if main window is realized from back-end\r
1558   return hwndMain != NULL;\r
1559 }\r
1560 \r
1561 void\r
1562 PopUpStartupDialog()\r
1563 {\r
1564     FARPROC lpProc;\r
1565     \r
1566     LoadLanguageFile(appData.language);\r
1567     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1568     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1569     FreeProcInstance(lpProc);\r
1570 }\r
1571 \r
1572 /*---------------------------------------------------------------------------*\\r
1573  *\r
1574  * GDI board drawing routines\r
1575  *\r
1576 \*---------------------------------------------------------------------------*/\r
1577 \r
1578 /* [AS] Draw square using background texture */\r
1579 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1580 {\r
1581     XFORM   x;\r
1582 \r
1583     if( mode == 0 ) {\r
1584         return; /* Should never happen! */\r
1585     }\r
1586 \r
1587     SetGraphicsMode( dst, GM_ADVANCED );\r
1588 \r
1589     switch( mode ) {\r
1590     case 1:\r
1591         /* Identity */\r
1592         break;\r
1593     case 2:\r
1594         /* X reflection */\r
1595         x.eM11 = -1.0;\r
1596         x.eM12 = 0;\r
1597         x.eM21 = 0;\r
1598         x.eM22 = 1.0;\r
1599         x.eDx = (FLOAT) dw + dx - 1;\r
1600         x.eDy = 0;\r
1601         dx = 0;\r
1602         SetWorldTransform( dst, &x );\r
1603         break;\r
1604     case 3:\r
1605         /* Y reflection */\r
1606         x.eM11 = 1.0;\r
1607         x.eM12 = 0;\r
1608         x.eM21 = 0;\r
1609         x.eM22 = -1.0;\r
1610         x.eDx = 0;\r
1611         x.eDy = (FLOAT) dh + dy - 1;\r
1612         dy = 0;\r
1613         SetWorldTransform( dst, &x );\r
1614         break;\r
1615     case 4:\r
1616         /* X/Y flip */\r
1617         x.eM11 = 0;\r
1618         x.eM12 = 1.0;\r
1619         x.eM21 = 1.0;\r
1620         x.eM22 = 0;\r
1621         x.eDx = (FLOAT) dx;\r
1622         x.eDy = (FLOAT) dy;\r
1623         dx = 0;\r
1624         dy = 0;\r
1625         SetWorldTransform( dst, &x );\r
1626         break;\r
1627     }\r
1628 \r
1629     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1630 \r
1631     x.eM11 = 1.0;\r
1632     x.eM12 = 0;\r
1633     x.eM21 = 0;\r
1634     x.eM22 = 1.0;\r
1635     x.eDx = 0;\r
1636     x.eDy = 0;\r
1637     SetWorldTransform( dst, &x );\r
1638 \r
1639     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1640 }\r
1641 \r
1642 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1643 enum {\r
1644     PM_WP = (int) WhitePawn, \r
1645     PM_WN = (int) WhiteKnight, \r
1646     PM_WB = (int) WhiteBishop, \r
1647     PM_WR = (int) WhiteRook, \r
1648     PM_WQ = (int) WhiteQueen, \r
1649     PM_WF = (int) WhiteFerz, \r
1650     PM_WW = (int) WhiteWazir, \r
1651     PM_WE = (int) WhiteAlfil, \r
1652     PM_WM = (int) WhiteMan, \r
1653     PM_WO = (int) WhiteCannon, \r
1654     PM_WU = (int) WhiteUnicorn, \r
1655     PM_WH = (int) WhiteNightrider, \r
1656     PM_WA = (int) WhiteAngel, \r
1657     PM_WC = (int) WhiteMarshall, \r
1658     PM_WAB = (int) WhiteCardinal, \r
1659     PM_WD = (int) WhiteDragon, \r
1660     PM_WL = (int) WhiteLance, \r
1661     PM_WS = (int) WhiteCobra, \r
1662     PM_WV = (int) WhiteFalcon, \r
1663     PM_WSG = (int) WhiteSilver, \r
1664     PM_WG = (int) WhiteGrasshopper, \r
1665     PM_WK = (int) WhiteKing,\r
1666     PM_BP = (int) BlackPawn, \r
1667     PM_BN = (int) BlackKnight, \r
1668     PM_BB = (int) BlackBishop, \r
1669     PM_BR = (int) BlackRook, \r
1670     PM_BQ = (int) BlackQueen, \r
1671     PM_BF = (int) BlackFerz, \r
1672     PM_BW = (int) BlackWazir, \r
1673     PM_BE = (int) BlackAlfil, \r
1674     PM_BM = (int) BlackMan,\r
1675     PM_BO = (int) BlackCannon, \r
1676     PM_BU = (int) BlackUnicorn, \r
1677     PM_BH = (int) BlackNightrider, \r
1678     PM_BA = (int) BlackAngel, \r
1679     PM_BC = (int) BlackMarshall, \r
1680     PM_BG = (int) BlackGrasshopper, \r
1681     PM_BAB = (int) BlackCardinal,\r
1682     PM_BD = (int) BlackDragon,\r
1683     PM_BL = (int) BlackLance,\r
1684     PM_BS = (int) BlackCobra,\r
1685     PM_BV = (int) BlackFalcon,\r
1686     PM_BSG = (int) BlackSilver,\r
1687     PM_BK = (int) BlackKing\r
1688 };\r
1689 \r
1690 static HFONT hPieceFont = NULL;\r
1691 static HBITMAP hPieceMask[(int) EmptySquare];\r
1692 static HBITMAP hPieceFace[(int) EmptySquare];\r
1693 static int fontBitmapSquareSize = 0;\r
1694 static char pieceToFontChar[(int) EmptySquare] =\r
1695                               { 'p', 'n', 'b', 'r', 'q', \r
1696                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1697                       'k', 'o', 'm', 'v', 't', 'w', \r
1698                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1699                                                               'l' };\r
1700 \r
1701 extern BOOL SetCharTable( char *table, const char * map );\r
1702 /* [HGM] moved to backend.c */\r
1703 \r
1704 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1705 {\r
1706     HBRUSH hbrush;\r
1707     BYTE r1 = GetRValue( color );\r
1708     BYTE g1 = GetGValue( color );\r
1709     BYTE b1 = GetBValue( color );\r
1710     BYTE r2 = r1 / 2;\r
1711     BYTE g2 = g1 / 2;\r
1712     BYTE b2 = b1 / 2;\r
1713     RECT rc;\r
1714 \r
1715     /* Create a uniform background first */\r
1716     hbrush = CreateSolidBrush( color );\r
1717     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1718     FillRect( hdc, &rc, hbrush );\r
1719     DeleteObject( hbrush );\r
1720     \r
1721     if( mode == 1 ) {\r
1722         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1723         int steps = squareSize / 2;\r
1724         int i;\r
1725 \r
1726         for( i=0; i<steps; i++ ) {\r
1727             BYTE r = r1 - (r1-r2) * i / steps;\r
1728             BYTE g = g1 - (g1-g2) * i / steps;\r
1729             BYTE b = b1 - (b1-b2) * i / steps;\r
1730 \r
1731             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1732             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1733             FillRect( hdc, &rc, hbrush );\r
1734             DeleteObject(hbrush);\r
1735         }\r
1736     }\r
1737     else if( mode == 2 ) {\r
1738         /* Diagonal gradient, good more or less for every piece */\r
1739         POINT triangle[3];\r
1740         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1741         HBRUSH hbrush_old;\r
1742         int steps = squareSize;\r
1743         int i;\r
1744 \r
1745         triangle[0].x = squareSize - steps;\r
1746         triangle[0].y = squareSize;\r
1747         triangle[1].x = squareSize;\r
1748         triangle[1].y = squareSize;\r
1749         triangle[2].x = squareSize;\r
1750         triangle[2].y = squareSize - steps;\r
1751 \r
1752         for( i=0; i<steps; i++ ) {\r
1753             BYTE r = r1 - (r1-r2) * i / steps;\r
1754             BYTE g = g1 - (g1-g2) * i / steps;\r
1755             BYTE b = b1 - (b1-b2) * i / steps;\r
1756 \r
1757             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1758             hbrush_old = SelectObject( hdc, hbrush );\r
1759             Polygon( hdc, triangle, 3 );\r
1760             SelectObject( hdc, hbrush_old );\r
1761             DeleteObject(hbrush);\r
1762             triangle[0].x++;\r
1763             triangle[2].y++;\r
1764         }\r
1765 \r
1766         SelectObject( hdc, hpen );\r
1767     }\r
1768 }\r
1769 \r
1770 /*\r
1771     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1772     seems to work ok. The main problem here is to find the "inside" of a chess\r
1773     piece: follow the steps as explained below.\r
1774 */\r
1775 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1776 {\r
1777     HBITMAP hbm;\r
1778     HBITMAP hbm_old;\r
1779     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1780     RECT rc;\r
1781     SIZE sz;\r
1782     POINT pt;\r
1783     int backColor = whitePieceColor; \r
1784     int foreColor = blackPieceColor;\r
1785     \r
1786     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1787         backColor = appData.fontBackColorWhite;\r
1788         foreColor = appData.fontForeColorWhite;\r
1789     }\r
1790     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1791         backColor = appData.fontBackColorBlack;\r
1792         foreColor = appData.fontForeColorBlack;\r
1793     }\r
1794 \r
1795     /* Mask */\r
1796     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1797 \r
1798     hbm_old = SelectObject( hdc, hbm );\r
1799 \r
1800     rc.left = 0;\r
1801     rc.top = 0;\r
1802     rc.right = squareSize;\r
1803     rc.bottom = squareSize;\r
1804 \r
1805     /* Step 1: background is now black */\r
1806     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1807 \r
1808     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1809 \r
1810     pt.x = (squareSize - sz.cx) / 2;\r
1811     pt.y = (squareSize - sz.cy) / 2;\r
1812 \r
1813     SetBkMode( hdc, TRANSPARENT );\r
1814     SetTextColor( hdc, chroma );\r
1815     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1816     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1817 \r
1818     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1819     /* Step 3: the area outside the piece is filled with white */\r
1820 //    FloodFill( hdc, 0, 0, chroma );\r
1821     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1822     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1823     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1824     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1825     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1826     /* \r
1827         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1828         but if the start point is not inside the piece we're lost!\r
1829         There should be a better way to do this... if we could create a region or path\r
1830         from the fill operation we would be fine for example.\r
1831     */\r
1832 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1833     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1834 \r
1835     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1836         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1837         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1838 \r
1839         SelectObject( dc2, bm2 );\r
1840         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1841         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1842         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1843         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1844         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1845 \r
1846         DeleteDC( dc2 );\r
1847         DeleteObject( bm2 );\r
1848     }\r
1849 \r
1850     SetTextColor( hdc, 0 );\r
1851     /* \r
1852         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1853         draw the piece again in black for safety.\r
1854     */\r
1855     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1856 \r
1857     SelectObject( hdc, hbm_old );\r
1858 \r
1859     if( hPieceMask[index] != NULL ) {\r
1860         DeleteObject( hPieceMask[index] );\r
1861     }\r
1862 \r
1863     hPieceMask[index] = hbm;\r
1864 \r
1865     /* Face */\r
1866     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1867 \r
1868     SelectObject( hdc, hbm );\r
1869 \r
1870     {\r
1871         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1872         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1873         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1874 \r
1875         SelectObject( dc1, hPieceMask[index] );\r
1876         SelectObject( dc2, bm2 );\r
1877         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1878         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1879         \r
1880         /* \r
1881             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1882             the piece background and deletes (makes transparent) the rest.\r
1883             Thanks to that mask, we are free to paint the background with the greates\r
1884             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1885             We use this, to make gradients and give the pieces a "roundish" look.\r
1886         */\r
1887         SetPieceBackground( hdc, backColor, 2 );\r
1888         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1889 \r
1890         DeleteDC( dc2 );\r
1891         DeleteDC( dc1 );\r
1892         DeleteObject( bm2 );\r
1893     }\r
1894 \r
1895     SetTextColor( hdc, foreColor );\r
1896     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1897 \r
1898     SelectObject( hdc, hbm_old );\r
1899 \r
1900     if( hPieceFace[index] != NULL ) {\r
1901         DeleteObject( hPieceFace[index] );\r
1902     }\r
1903 \r
1904     hPieceFace[index] = hbm;\r
1905 }\r
1906 \r
1907 static int TranslatePieceToFontPiece( int piece )\r
1908 {\r
1909     switch( piece ) {\r
1910     case BlackPawn:\r
1911         return PM_BP;\r
1912     case BlackKnight:\r
1913         return PM_BN;\r
1914     case BlackBishop:\r
1915         return PM_BB;\r
1916     case BlackRook:\r
1917         return PM_BR;\r
1918     case BlackQueen:\r
1919         return PM_BQ;\r
1920     case BlackKing:\r
1921         return PM_BK;\r
1922     case WhitePawn:\r
1923         return PM_WP;\r
1924     case WhiteKnight:\r
1925         return PM_WN;\r
1926     case WhiteBishop:\r
1927         return PM_WB;\r
1928     case WhiteRook:\r
1929         return PM_WR;\r
1930     case WhiteQueen:\r
1931         return PM_WQ;\r
1932     case WhiteKing:\r
1933         return PM_WK;\r
1934 \r
1935     case BlackAngel:\r
1936         return PM_BA;\r
1937     case BlackMarshall:\r
1938         return PM_BC;\r
1939     case BlackFerz:\r
1940         return PM_BF;\r
1941     case BlackNightrider:\r
1942         return PM_BH;\r
1943     case BlackAlfil:\r
1944         return PM_BE;\r
1945     case BlackWazir:\r
1946         return PM_BW;\r
1947     case BlackUnicorn:\r
1948         return PM_BU;\r
1949     case BlackCannon:\r
1950         return PM_BO;\r
1951     case BlackGrasshopper:\r
1952         return PM_BG;\r
1953     case BlackMan:\r
1954         return PM_BM;\r
1955     case BlackSilver:\r
1956         return PM_BSG;\r
1957     case BlackLance:\r
1958         return PM_BL;\r
1959     case BlackFalcon:\r
1960         return PM_BV;\r
1961     case BlackCobra:\r
1962         return PM_BS;\r
1963     case BlackCardinal:\r
1964         return PM_BAB;\r
1965     case BlackDragon:\r
1966         return PM_BD;\r
1967 \r
1968     case WhiteAngel:\r
1969         return PM_WA;\r
1970     case WhiteMarshall:\r
1971         return PM_WC;\r
1972     case WhiteFerz:\r
1973         return PM_WF;\r
1974     case WhiteNightrider:\r
1975         return PM_WH;\r
1976     case WhiteAlfil:\r
1977         return PM_WE;\r
1978     case WhiteWazir:\r
1979         return PM_WW;\r
1980     case WhiteUnicorn:\r
1981         return PM_WU;\r
1982     case WhiteCannon:\r
1983         return PM_WO;\r
1984     case WhiteGrasshopper:\r
1985         return PM_WG;\r
1986     case WhiteMan:\r
1987         return PM_WM;\r
1988     case WhiteSilver:\r
1989         return PM_WSG;\r
1990     case WhiteLance:\r
1991         return PM_WL;\r
1992     case WhiteFalcon:\r
1993         return PM_WV;\r
1994     case WhiteCobra:\r
1995         return PM_WS;\r
1996     case WhiteCardinal:\r
1997         return PM_WAB;\r
1998     case WhiteDragon:\r
1999         return PM_WD;\r
2000     }\r
2001 \r
2002     return 0;\r
2003 }\r
2004 \r
2005 void CreatePiecesFromFont()\r
2006 {\r
2007     LOGFONT lf;\r
2008     HDC hdc_window = NULL;\r
2009     HDC hdc = NULL;\r
2010     HFONT hfont_old;\r
2011     int fontHeight;\r
2012     int i;\r
2013 \r
2014     if( fontBitmapSquareSize < 0 ) {\r
2015         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2016         return;\r
2017     }\r
2018 \r
2019     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2020             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2021         fontBitmapSquareSize = -1;\r
2022         return;\r
2023     }\r
2024 \r
2025     if( fontBitmapSquareSize != squareSize ) {\r
2026         hdc_window = GetDC( hwndMain );\r
2027         hdc = CreateCompatibleDC( hdc_window );\r
2028 \r
2029         if( hPieceFont != NULL ) {\r
2030             DeleteObject( hPieceFont );\r
2031         }\r
2032         else {\r
2033             for( i=0; i<=(int)BlackKing; i++ ) {\r
2034                 hPieceMask[i] = NULL;\r
2035                 hPieceFace[i] = NULL;\r
2036             }\r
2037         }\r
2038 \r
2039         fontHeight = 75;\r
2040 \r
2041         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2042             fontHeight = appData.fontPieceSize;\r
2043         }\r
2044 \r
2045         fontHeight = (fontHeight * squareSize) / 100;\r
2046 \r
2047         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2048         lf.lfWidth = 0;\r
2049         lf.lfEscapement = 0;\r
2050         lf.lfOrientation = 0;\r
2051         lf.lfWeight = FW_NORMAL;\r
2052         lf.lfItalic = 0;\r
2053         lf.lfUnderline = 0;\r
2054         lf.lfStrikeOut = 0;\r
2055         lf.lfCharSet = DEFAULT_CHARSET;\r
2056         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2057         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2058         lf.lfQuality = PROOF_QUALITY;\r
2059         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2060         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2061         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2062 \r
2063         hPieceFont = CreateFontIndirect( &lf );\r
2064 \r
2065         if( hPieceFont == NULL ) {\r
2066             fontBitmapSquareSize = -2;\r
2067         }\r
2068         else {\r
2069             /* Setup font-to-piece character table */\r
2070             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2071                 /* No (or wrong) global settings, try to detect the font */\r
2072                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2073                     /* Alpha */\r
2074                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2075                 }\r
2076                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2077                     /* DiagramTT* family */\r
2078                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2079                 }\r
2080                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2081                     /* Fairy symbols */\r
2082                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2083                 }\r
2084                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2085                     /* Good Companion (Some characters get warped as literal :-( */\r
2086                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2087                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2088                     SetCharTable(pieceToFontChar, s);\r
2089                 }\r
2090                 else {\r
2091                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2092                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2093                 }\r
2094             }\r
2095 \r
2096             /* Create bitmaps */\r
2097             hfont_old = SelectObject( hdc, hPieceFont );\r
2098             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2099                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2100                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2101 \r
2102             SelectObject( hdc, hfont_old );\r
2103 \r
2104             fontBitmapSquareSize = squareSize;\r
2105         }\r
2106     }\r
2107 \r
2108     if( hdc != NULL ) {\r
2109         DeleteDC( hdc );\r
2110     }\r
2111 \r
2112     if( hdc_window != NULL ) {\r
2113         ReleaseDC( hwndMain, hdc_window );\r
2114     }\r
2115 }\r
2116 \r
2117 HBITMAP\r
2118 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2119 {\r
2120   char name[128], buf[MSG_SIZ];\r
2121 \r
2122     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2123   if(appData.pieceDirectory[0]) {\r
2124     HBITMAP res;\r
2125     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2126     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2127     if(res) return res;\r
2128   }\r
2129   if (gameInfo.event &&\r
2130       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2131       strcmp(name, "k80s") == 0) {\r
2132     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2133   }\r
2134   return LoadBitmap(hinst, name);\r
2135 }\r
2136 \r
2137 \r
2138 /* Insert a color into the program's logical palette\r
2139    structure.  This code assumes the given color is\r
2140    the result of the RGB or PALETTERGB macro, and it\r
2141    knows how those macros work (which is documented).\r
2142 */\r
2143 VOID\r
2144 InsertInPalette(COLORREF color)\r
2145 {\r
2146   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2147 \r
2148   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2149     DisplayFatalError(_("Too many colors"), 0, 1);\r
2150     pLogPal->palNumEntries--;\r
2151     return;\r
2152   }\r
2153 \r
2154   pe->peFlags = (char) 0;\r
2155   pe->peRed = (char) (0xFF & color);\r
2156   pe->peGreen = (char) (0xFF & (color >> 8));\r
2157   pe->peBlue = (char) (0xFF & (color >> 16));\r
2158   return;\r
2159 }\r
2160 \r
2161 \r
2162 VOID\r
2163 InitDrawingColors()\r
2164 {\r
2165   if (pLogPal == NULL) {\r
2166     /* Allocate enough memory for a logical palette with\r
2167      * PALETTESIZE entries and set the size and version fields\r
2168      * of the logical palette structure.\r
2169      */\r
2170     pLogPal = (NPLOGPALETTE)\r
2171       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2172                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2173     pLogPal->palVersion    = 0x300;\r
2174   }\r
2175   pLogPal->palNumEntries = 0;\r
2176 \r
2177   InsertInPalette(lightSquareColor);\r
2178   InsertInPalette(darkSquareColor);\r
2179   InsertInPalette(whitePieceColor);\r
2180   InsertInPalette(blackPieceColor);\r
2181   InsertInPalette(highlightSquareColor);\r
2182   InsertInPalette(premoveHighlightColor);\r
2183 \r
2184   /*  create a logical color palette according the information\r
2185    *  in the LOGPALETTE structure.\r
2186    */\r
2187   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2188 \r
2189   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2190   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2191   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2192   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2193   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2194   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2195   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2196   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2197   /* [AS] Force rendering of the font-based pieces */\r
2198   if( fontBitmapSquareSize > 0 ) {\r
2199     fontBitmapSquareSize = 0;\r
2200   }\r
2201 }\r
2202 \r
2203 \r
2204 int\r
2205 BoardWidth(int boardSize, int n)\r
2206 { /* [HGM] argument n added to allow different width and height */\r
2207   int lineGap = sizeInfo[boardSize].lineGap;\r
2208 \r
2209   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2210       lineGap = appData.overrideLineGap;\r
2211   }\r
2212 \r
2213   return (n + 1) * lineGap +\r
2214           n * sizeInfo[boardSize].squareSize;\r
2215 }\r
2216 \r
2217 /* Respond to board resize by dragging edge */\r
2218 VOID\r
2219 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2220 {\r
2221   BoardSize newSize = NUM_SIZES - 1;\r
2222   static int recurse = 0;\r
2223   if (IsIconic(hwndMain)) return;\r
2224   if (recurse > 0) return;\r
2225   recurse++;\r
2226   while (newSize > 0) {\r
2227         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2228         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2229            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2230     newSize--;\r
2231   } \r
2232   boardSize = newSize;\r
2233   InitDrawingSizes(boardSize, flags);\r
2234   recurse--;\r
2235 }\r
2236 \r
2237 \r
2238 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2239 \r
2240 VOID\r
2241 InitDrawingSizes(BoardSize boardSize, int flags)\r
2242 {\r
2243   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2244   ChessSquare piece;\r
2245   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2246   HDC hdc;\r
2247   SIZE clockSize, messageSize;\r
2248   HFONT oldFont;\r
2249   char buf[MSG_SIZ];\r
2250   char *str;\r
2251   HMENU hmenu = GetMenu(hwndMain);\r
2252   RECT crect, wrect, oldRect;\r
2253   int offby;\r
2254   LOGBRUSH logbrush;\r
2255   VariantClass v = gameInfo.variant;\r
2256 \r
2257   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2258   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2259 \r
2260   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2261   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2262   oldBoardSize = boardSize;\r
2263 \r
2264   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2265   { // correct board size to one where built-in pieces exist\r
2266     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2267        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2268       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2269       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2270       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2271       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2272       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2273                                    boardSize = SizeMiddling;\r
2274     }\r
2275   }\r
2276   if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2277 \r
2278   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2279   oldRect.top = wpMain.y;\r
2280   oldRect.right = wpMain.x + wpMain.width;\r
2281   oldRect.bottom = wpMain.y + wpMain.height;\r
2282 \r
2283   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2284   smallLayout = sizeInfo[boardSize].smallLayout;\r
2285   squareSize = sizeInfo[boardSize].squareSize;\r
2286   lineGap = sizeInfo[boardSize].lineGap;\r
2287   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2288   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2289 \r
2290   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2291       lineGap = appData.overrideLineGap;\r
2292   }\r
2293 \r
2294   if (tinyLayout != oldTinyLayout) {\r
2295     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2296     if (tinyLayout) {\r
2297       style &= ~WS_SYSMENU;\r
2298       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2299                  "&Minimize\tCtrl+F4");\r
2300     } else {\r
2301       style |= WS_SYSMENU;\r
2302       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2303     }\r
2304     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2305 \r
2306     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2307       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2308         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2309     }\r
2310     DrawMenuBar(hwndMain);\r
2311   }\r
2312 \r
2313   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2314   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2315 \r
2316   /* Get text area sizes */\r
2317   hdc = GetDC(hwndMain);\r
2318   if (appData.clockMode) {\r
2319     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2320   } else {\r
2321     snprintf(buf, MSG_SIZ, _("White"));\r
2322   }\r
2323   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2324   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2325   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2326   str = _("We only care about the height here");\r
2327   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2328   SelectObject(hdc, oldFont);\r
2329   ReleaseDC(hwndMain, hdc);\r
2330 \r
2331   /* Compute where everything goes */\r
2332   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2333         /* [HGM] logo: if either logo is on, reserve space for it */\r
2334         logoHeight =  2*clockSize.cy;\r
2335         leftLogoRect.left   = OUTER_MARGIN;\r
2336         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2337         leftLogoRect.top    = OUTER_MARGIN;\r
2338         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2339 \r
2340         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2341         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2342         rightLogoRect.top    = OUTER_MARGIN;\r
2343         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2344 \r
2345 \r
2346     whiteRect.left = leftLogoRect.right;\r
2347     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2348     whiteRect.top = OUTER_MARGIN;\r
2349     whiteRect.bottom = whiteRect.top + logoHeight;\r
2350 \r
2351     blackRect.right = rightLogoRect.left;\r
2352     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2353     blackRect.top = whiteRect.top;\r
2354     blackRect.bottom = whiteRect.bottom;\r
2355   } else {\r
2356     whiteRect.left = OUTER_MARGIN;\r
2357     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2358     whiteRect.top = OUTER_MARGIN;\r
2359     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2360 \r
2361     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2362     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2363     blackRect.top = whiteRect.top;\r
2364     blackRect.bottom = whiteRect.bottom;\r
2365 \r
2366     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2367   }\r
2368 \r
2369   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2370   if (appData.showButtonBar) {\r
2371     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2372       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2373   } else {\r
2374     messageRect.right = OUTER_MARGIN + boardWidth;\r
2375   }\r
2376   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2377   messageRect.bottom = messageRect.top + messageSize.cy;\r
2378 \r
2379   boardRect.left = OUTER_MARGIN;\r
2380   boardRect.right = boardRect.left + boardWidth;\r
2381   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2382   boardRect.bottom = boardRect.top + boardHeight;\r
2383 \r
2384   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2385   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2386   oldTinyLayout = tinyLayout;\r
2387   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2388   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2389     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2390   winW *= 1 + twoBoards;\r
2391   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2392   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2393   wpMain.height = winH; //       without disturbing window attachments\r
2394   GetWindowRect(hwndMain, &wrect);\r
2395   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2396                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2397 \r
2398   // [HGM] placement: let attached windows follow size change.\r
2399   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2400   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2401   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2402   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2403   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2404 \r
2405   /* compensate if menu bar wrapped */\r
2406   GetClientRect(hwndMain, &crect);\r
2407   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2408   wpMain.height += offby;\r
2409   switch (flags) {\r
2410   case WMSZ_TOPLEFT:\r
2411     SetWindowPos(hwndMain, NULL, \r
2412                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2413                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2414     break;\r
2415 \r
2416   case WMSZ_TOPRIGHT:\r
2417   case WMSZ_TOP:\r
2418     SetWindowPos(hwndMain, NULL, \r
2419                  wrect.left, wrect.bottom - wpMain.height, \r
2420                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2421     break;\r
2422 \r
2423   case WMSZ_BOTTOMLEFT:\r
2424   case WMSZ_LEFT:\r
2425     SetWindowPos(hwndMain, NULL, \r
2426                  wrect.right - wpMain.width, wrect.top, \r
2427                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2428     break;\r
2429 \r
2430   case WMSZ_BOTTOMRIGHT:\r
2431   case WMSZ_BOTTOM:\r
2432   case WMSZ_RIGHT:\r
2433   default:\r
2434     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2435                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2436     break;\r
2437   }\r
2438 \r
2439   hwndPause = NULL;\r
2440   for (i = 0; i < N_BUTTONS; i++) {\r
2441     if (buttonDesc[i].hwnd != NULL) {\r
2442       DestroyWindow(buttonDesc[i].hwnd);\r
2443       buttonDesc[i].hwnd = NULL;\r
2444     }\r
2445     if (appData.showButtonBar) {\r
2446       buttonDesc[i].hwnd =\r
2447         CreateWindow("BUTTON", buttonDesc[i].label,\r
2448                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2449                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2450                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2451                      (HMENU) buttonDesc[i].id,\r
2452                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2453       if (tinyLayout) {\r
2454         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2455                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2456                     MAKELPARAM(FALSE, 0));\r
2457       }\r
2458       if (buttonDesc[i].id == IDM_Pause)\r
2459         hwndPause = buttonDesc[i].hwnd;\r
2460       buttonDesc[i].wndproc = (WNDPROC)\r
2461         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2462     }\r
2463   }\r
2464   if (gridPen != NULL) DeleteObject(gridPen);\r
2465   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2466   if (premovePen != NULL) DeleteObject(premovePen);\r
2467   if (lineGap != 0) {\r
2468     logbrush.lbStyle = BS_SOLID;\r
2469     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2470     gridPen =\r
2471       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2472                    lineGap, &logbrush, 0, NULL);\r
2473     logbrush.lbColor = highlightSquareColor;\r
2474     highlightPen =\r
2475       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2476                    lineGap, &logbrush, 0, NULL);\r
2477 \r
2478     logbrush.lbColor = premoveHighlightColor; \r
2479     premovePen =\r
2480       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2481                    lineGap, &logbrush, 0, NULL);\r
2482 \r
2483     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2484     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2485       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2486       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2487         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2488       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2489         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2490       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2491     }\r
2492     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2493       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2494       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2495         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2496         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2497       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2498         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2499       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2500     }\r
2501   }\r
2502 \r
2503   /* [HGM] Licensing requirement */\r
2504 #ifdef GOTHIC\r
2505   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2506 #endif\r
2507 #ifdef FALCON\r
2508   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2509 #endif\r
2510   GothicPopUp( "", VariantNormal);\r
2511 \r
2512 \r
2513 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2514 \r
2515   /* Load piece bitmaps for this board size */\r
2516   for (i=0; i<=2; i++) {\r
2517     for (piece = WhitePawn;\r
2518          (int) piece < (int) BlackPawn;\r
2519          piece = (ChessSquare) ((int) piece + 1)) {\r
2520       if (pieceBitmap[i][piece] != NULL)\r
2521         DeleteObject(pieceBitmap[i][piece]);\r
2522     }\r
2523   }\r
2524 \r
2525   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2526   // Orthodox Chess pieces\r
2527   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2528   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2529   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2530   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2531   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2532   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2533   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2534   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2535   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2536   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2537   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2538   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2539   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2540   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2541   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2542   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2543     // in Shogi, Hijack the unused Queen for Lance\r
2544     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2545     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2546     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2547   } else {\r
2548     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2549     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2550     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2551   }\r
2552 \r
2553   if(squareSize <= 72 && squareSize >= 33) { \r
2554     /* A & C are available in most sizes now */\r
2555     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2556       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2557       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2558       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2559       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2560       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2561       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2562       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2563       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2564       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2565       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2566       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2567       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2568     } else { // Smirf-like\r
2569       if(gameInfo.variant == VariantSChess) {\r
2570         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2571         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2572         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2573       } else {\r
2574         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2575         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2576         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2577       }\r
2578     }\r
2579     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2580       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2581       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2582       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2583     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2584       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2585       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2586       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2587     } else { // WinBoard standard\r
2588       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2589       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2590       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2591     }\r
2592   }\r
2593 \r
2594 \r
2595   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2596     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2597     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2598     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2599     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2600     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2601     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2602     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2603     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2604     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2605     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2606     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2607     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2608     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2609     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2610     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2611     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2612     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2613     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2614     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2615     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2616     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2617     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2618     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2619     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2620     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2621     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2622     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2623     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2624     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2625     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2626 \r
2627     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2628       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2629       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2630       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2631       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2632       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2633       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2634       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2635       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2636       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2637       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2638       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2639       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2640     } else {\r
2641       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2642       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2643       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2644       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2645       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2646       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2647       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2648       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2649       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2650       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2651       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2652       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2653     }\r
2654 \r
2655   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2656     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2657     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2658     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2659     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2660     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2661     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2662     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2663     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2664     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2665     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2666     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2667     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2668     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2669     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2670   }\r
2671 \r
2672 \r
2673   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2674   /* special Shogi support in this size */\r
2675   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2676       for (piece = WhitePawn;\r
2677            (int) piece < (int) BlackPawn;\r
2678            piece = (ChessSquare) ((int) piece + 1)) {\r
2679         if (pieceBitmap[i][piece] != NULL)\r
2680           DeleteObject(pieceBitmap[i][piece]);\r
2681       }\r
2682     }\r
2683   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2684   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2685   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2686   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2687   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2688   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2689   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2690   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2691   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2692   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2693   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2694   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2695   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2696   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2697   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2698   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2699   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2700   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2701   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2702   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2703   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2704   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2705   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2706   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2707   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2708   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2709   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2710   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2711   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2712   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2713   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2714   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2715   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2716   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2717   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2718   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2719   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2720   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2721   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2722   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2723   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2724   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2725   minorSize = 0;\r
2726   }\r
2727 }\r
2728 \r
2729 HBITMAP\r
2730 PieceBitmap(ChessSquare p, int kind)\r
2731 {\r
2732   if ((int) p >= (int) BlackPawn)\r
2733     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2734 \r
2735   return pieceBitmap[kind][(int) p];\r
2736 }\r
2737 \r
2738 /***************************************************************/\r
2739 \r
2740 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2741 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2742 /*\r
2743 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2744 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2745 */\r
2746 \r
2747 VOID\r
2748 SquareToPos(int row, int column, int * x, int * y)\r
2749 {\r
2750   if (flipView) {\r
2751     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2752     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2753   } else {\r
2754     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2755     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2756   }\r
2757 }\r
2758 \r
2759 VOID\r
2760 DrawCoordsOnDC(HDC hdc)\r
2761 {\r
2762   static char files[] = "0123456789012345678901221098765432109876543210";\r
2763   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2764   char str[2] = { NULLCHAR, NULLCHAR };\r
2765   int oldMode, oldAlign, x, y, start, i;\r
2766   HFONT oldFont;\r
2767   HBRUSH oldBrush;\r
2768 \r
2769   if (!appData.showCoords)\r
2770     return;\r
2771 \r
2772   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2773 \r
2774   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2775   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2776   oldAlign = GetTextAlign(hdc);\r
2777   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2778 \r
2779   y = boardRect.top + lineGap;\r
2780   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2781 \r
2782   if(border) {\r
2783     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2784     x += border - lineGap - 4; y += squareSize - 6;\r
2785   } else\r
2786   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2787   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2788     str[0] = files[start + i];\r
2789     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2790     y += squareSize + lineGap;\r
2791   }\r
2792 \r
2793   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2794 \r
2795   if(border) {\r
2796     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2797     x += -border + 4; y += border - squareSize + 6;\r
2798   } else\r
2799   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2800   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2801     str[0] = ranks[start + i];\r
2802     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2803     x += squareSize + lineGap;\r
2804   }    \r
2805 \r
2806   SelectObject(hdc, oldBrush);\r
2807   SetBkMode(hdc, oldMode);\r
2808   SetTextAlign(hdc, oldAlign);\r
2809   SelectObject(hdc, oldFont);\r
2810 }\r
2811 \r
2812 VOID\r
2813 DrawGridOnDC(HDC hdc)\r
2814 {\r
2815   HPEN oldPen;\r
2816  \r
2817   if (lineGap != 0) {\r
2818     oldPen = SelectObject(hdc, gridPen);\r
2819     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2820     SelectObject(hdc, oldPen);\r
2821   }\r
2822 }\r
2823 \r
2824 #define HIGHLIGHT_PEN 0\r
2825 #define PREMOVE_PEN   1\r
2826 \r
2827 VOID\r
2828 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2829 {\r
2830   int x1, y1;\r
2831   HPEN oldPen, hPen;\r
2832   if (lineGap == 0) return;\r
2833   if (flipView) {\r
2834     x1 = boardRect.left +\r
2835       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2836     y1 = boardRect.top +\r
2837       lineGap/2 + y * (squareSize + lineGap) + border;\r
2838   } else {\r
2839     x1 = boardRect.left +\r
2840       lineGap/2 + x * (squareSize + lineGap) + border;\r
2841     y1 = boardRect.top +\r
2842       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2843   }\r
2844   hPen = pen ? premovePen : highlightPen;\r
2845   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2846   MoveToEx(hdc, x1, y1, NULL);\r
2847   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2848   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2849   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2850   LineTo(hdc, x1, y1);\r
2851   SelectObject(hdc, oldPen);\r
2852 }\r
2853 \r
2854 VOID\r
2855 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2856 {\r
2857   int i;\r
2858   for (i=0; i<2; i++) {\r
2859     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2860       DrawHighlightOnDC(hdc, TRUE,\r
2861                         h->sq[i].x, h->sq[i].y,\r
2862                         pen);\r
2863   }\r
2864 }\r
2865 \r
2866 /* Note: sqcolor is used only in monoMode */\r
2867 /* Note that this code is largely duplicated in woptions.c,\r
2868    function DrawSampleSquare, so that needs to be updated too */\r
2869 VOID\r
2870 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2871 {\r
2872   HBITMAP oldBitmap;\r
2873   HBRUSH oldBrush;\r
2874   int tmpSize;\r
2875 \r
2876   if (appData.blindfold) return;\r
2877 \r
2878   /* [AS] Use font-based pieces if needed */\r
2879   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2880     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2881     CreatePiecesFromFont();\r
2882 \r
2883     if( fontBitmapSquareSize == squareSize ) {\r
2884         int index = TranslatePieceToFontPiece(piece);\r
2885 \r
2886         SelectObject( tmphdc, hPieceMask[ index ] );\r
2887 \r
2888       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2889         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2890       else\r
2891         BitBlt( hdc,\r
2892             x, y,\r
2893             squareSize, squareSize,\r
2894             tmphdc,\r
2895             0, 0,\r
2896             SRCAND );\r
2897 \r
2898         SelectObject( tmphdc, hPieceFace[ index ] );\r
2899 \r
2900       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2901         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2902       else\r
2903         BitBlt( hdc,\r
2904             x, y,\r
2905             squareSize, squareSize,\r
2906             tmphdc,\r
2907             0, 0,\r
2908             SRCPAINT );\r
2909 \r
2910         return;\r
2911     }\r
2912   }\r
2913 \r
2914   if (appData.monoMode) {\r
2915     SelectObject(tmphdc, PieceBitmap(piece, \r
2916       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2917     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2918            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2919   } else {\r
2920     HBRUSH xBrush = whitePieceBrush;\r
2921     tmpSize = squareSize;\r
2922     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2923     if(minorSize &&\r
2924         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2925          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2926       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2927       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2928       x += (squareSize - minorSize)>>1;\r
2929       y += squareSize - minorSize - 2;\r
2930       tmpSize = minorSize;\r
2931     }\r
2932     if (color || appData.allWhite ) {\r
2933       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2934       if( color )\r
2935               oldBrush = SelectObject(hdc, xBrush);\r
2936       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2937       if(appData.upsideDown && color==flipView)\r
2938         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2939       else\r
2940         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2941       /* Use black for outline of white pieces */\r
2942       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2943       if(appData.upsideDown && color==flipView)\r
2944         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2945       else\r
2946         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2947     } else if(appData.pieceDirectory[0]) {\r
2948       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2949       oldBrush = SelectObject(hdc, xBrush);\r
2950       if(appData.upsideDown && color==flipView)\r
2951         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2952       else\r
2953         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2954       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2955       if(appData.upsideDown && color==flipView)\r
2956         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2957       else\r
2958         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2959     } else {\r
2960       /* Use square color for details of black pieces */\r
2961       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2962       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2963       if(appData.upsideDown && !flipView)\r
2964         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2965       else\r
2966         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2967     }\r
2968     SelectObject(hdc, oldBrush);\r
2969     SelectObject(tmphdc, oldBitmap);\r
2970   }\r
2971 }\r
2972 \r
2973 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2974 int GetBackTextureMode( int algo )\r
2975 {\r
2976     int result = BACK_TEXTURE_MODE_DISABLED;\r
2977 \r
2978     switch( algo ) \r
2979     {\r
2980         case BACK_TEXTURE_MODE_PLAIN:\r
2981             result = 1; /* Always use identity map */\r
2982             break;\r
2983         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2984             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2985             break;\r
2986     }\r
2987 \r
2988     return result;\r
2989 }\r
2990 \r
2991 /* \r
2992     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2993     to handle redraws cleanly (as random numbers would always be different).\r
2994 */\r
2995 VOID RebuildTextureSquareInfo()\r
2996 {\r
2997     BITMAP bi;\r
2998     int lite_w = 0;\r
2999     int lite_h = 0;\r
3000     int dark_w = 0;\r
3001     int dark_h = 0;\r
3002     int row;\r
3003     int col;\r
3004 \r
3005     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3006 \r
3007     if( liteBackTexture != NULL ) {\r
3008         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3009             lite_w = bi.bmWidth;\r
3010             lite_h = bi.bmHeight;\r
3011         }\r
3012     }\r
3013 \r
3014     if( darkBackTexture != NULL ) {\r
3015         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3016             dark_w = bi.bmWidth;\r
3017             dark_h = bi.bmHeight;\r
3018         }\r
3019     }\r
3020 \r
3021     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3022         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3023             if( (col + row) & 1 ) {\r
3024                 /* Lite square */\r
3025                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3026                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3027                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3028                   else\r
3029                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3030                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3031                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3032                   else\r
3033                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3034                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3035                 }\r
3036             }\r
3037             else {\r
3038                 /* Dark square */\r
3039                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3040                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3041                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3042                   else\r
3043                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3044                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3045                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3046                   else\r
3047                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3048                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3049                 }\r
3050             }\r
3051         }\r
3052     }\r
3053 }\r
3054 \r
3055 /* [AS] Arrow highlighting support */\r
3056 \r
3057 static double A_WIDTH = 5; /* Width of arrow body */\r
3058 \r
3059 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3060 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3061 \r
3062 static double Sqr( double x )\r
3063 {\r
3064     return x*x;\r
3065 }\r
3066 \r
3067 static int Round( double x )\r
3068 {\r
3069     return (int) (x + 0.5);\r
3070 }\r
3071 \r
3072 /* Draw an arrow between two points using current settings */\r
3073 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3074 {\r
3075     POINT arrow[7];\r
3076     double dx, dy, j, k, x, y;\r
3077 \r
3078     if( d_x == s_x ) {\r
3079         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3080 \r
3081         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3082         arrow[0].y = s_y;\r
3083 \r
3084         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3085         arrow[1].y = d_y - h;\r
3086 \r
3087         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3088         arrow[2].y = d_y - h;\r
3089 \r
3090         arrow[3].x = d_x;\r
3091         arrow[3].y = d_y;\r
3092 \r
3093         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3094         arrow[5].y = d_y - h;\r
3095 \r
3096         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3097         arrow[4].y = d_y - h;\r
3098 \r
3099         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3100         arrow[6].y = s_y;\r
3101     }\r
3102     else if( d_y == s_y ) {\r
3103         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3104 \r
3105         arrow[0].x = s_x;\r
3106         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3107 \r
3108         arrow[1].x = d_x - w;\r
3109         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3110 \r
3111         arrow[2].x = d_x - w;\r
3112         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3113 \r
3114         arrow[3].x = d_x;\r
3115         arrow[3].y = d_y;\r
3116 \r
3117         arrow[5].x = d_x - w;\r
3118         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3119 \r
3120         arrow[4].x = d_x - w;\r
3121         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3122 \r
3123         arrow[6].x = s_x;\r
3124         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3125     }\r
3126     else {\r
3127         /* [AS] Needed a lot of paper for this! :-) */\r
3128         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3129         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3130   \r
3131         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3132 \r
3133         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3134 \r
3135         x = s_x;\r
3136         y = s_y;\r
3137 \r
3138         arrow[0].x = Round(x - j);\r
3139         arrow[0].y = Round(y + j*dx);\r
3140 \r
3141         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3142         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3143 \r
3144         if( d_x > s_x ) {\r
3145             x = (double) d_x - k;\r
3146             y = (double) d_y - k*dy;\r
3147         }\r
3148         else {\r
3149             x = (double) d_x + k;\r
3150             y = (double) d_y + k*dy;\r
3151         }\r
3152 \r
3153         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3154 \r
3155         arrow[6].x = Round(x - j);\r
3156         arrow[6].y = Round(y + j*dx);\r
3157 \r
3158         arrow[2].x = Round(arrow[6].x + 2*j);\r
3159         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3160 \r
3161         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3162         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3163 \r
3164         arrow[4].x = d_x;\r
3165         arrow[4].y = d_y;\r
3166 \r
3167         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3168         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3169     }\r
3170 \r
3171     Polygon( hdc, arrow, 7 );\r
3172 }\r
3173 \r
3174 /* [AS] Draw an arrow between two squares */\r
3175 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3176 {\r
3177     int s_x, s_y, d_x, d_y;\r
3178     HPEN hpen;\r
3179     HPEN holdpen;\r
3180     HBRUSH hbrush;\r
3181     HBRUSH holdbrush;\r
3182     LOGBRUSH stLB;\r
3183 \r
3184     if( s_col == d_col && s_row == d_row ) {\r
3185         return;\r
3186     }\r
3187 \r
3188     /* Get source and destination points */\r
3189     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3190     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3191 \r
3192     if( d_y > s_y ) {\r
3193         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3194     }\r
3195     else if( d_y < s_y ) {\r
3196         d_y += squareSize / 2 + squareSize / 4;\r
3197     }\r
3198     else {\r
3199         d_y += squareSize / 2;\r
3200     }\r
3201 \r
3202     if( d_x > s_x ) {\r
3203         d_x += squareSize / 2 - squareSize / 4;\r
3204     }\r
3205     else if( d_x < s_x ) {\r
3206         d_x += squareSize / 2 + squareSize / 4;\r
3207     }\r
3208     else {\r
3209         d_x += squareSize / 2;\r
3210     }\r
3211 \r
3212     s_x += squareSize / 2;\r
3213     s_y += squareSize / 2;\r
3214 \r
3215     /* Adjust width */\r
3216     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3217 \r
3218     /* Draw */\r
3219     stLB.lbStyle = BS_SOLID;\r
3220     stLB.lbColor = appData.highlightArrowColor;\r
3221     stLB.lbHatch = 0;\r
3222 \r
3223     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3224     holdpen = SelectObject( hdc, hpen );\r
3225     hbrush = CreateBrushIndirect( &stLB );\r
3226     holdbrush = SelectObject( hdc, hbrush );\r
3227 \r
3228     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3229 \r
3230     SelectObject( hdc, holdpen );\r
3231     SelectObject( hdc, holdbrush );\r
3232     DeleteObject( hpen );\r
3233     DeleteObject( hbrush );\r
3234 }\r
3235 \r
3236 BOOL HasHighlightInfo()\r
3237 {\r
3238     BOOL result = FALSE;\r
3239 \r
3240     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3241         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3242     {\r
3243         result = TRUE;\r
3244     }\r
3245 \r
3246     return result;\r
3247 }\r
3248 \r
3249 BOOL IsDrawArrowEnabled()\r
3250 {\r
3251     BOOL result = FALSE;\r
3252 \r
3253     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3254         result = TRUE;\r
3255     }\r
3256 \r
3257     return result;\r
3258 }\r
3259 \r
3260 VOID DrawArrowHighlight( HDC hdc )\r
3261 {\r
3262     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3263         DrawArrowBetweenSquares( hdc,\r
3264             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3265             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3266     }\r
3267 }\r
3268 \r
3269 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3270 {\r
3271     HRGN result = NULL;\r
3272 \r
3273     if( HasHighlightInfo() ) {\r
3274         int x1, y1, x2, y2;\r
3275         int sx, sy, dx, dy;\r
3276 \r
3277         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3278         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3279 \r
3280         sx = MIN( x1, x2 );\r
3281         sy = MIN( y1, y2 );\r
3282         dx = MAX( x1, x2 ) + squareSize;\r
3283         dy = MAX( y1, y2 ) + squareSize;\r
3284 \r
3285         result = CreateRectRgn( sx, sy, dx, dy );\r
3286     }\r
3287 \r
3288     return result;\r
3289 }\r
3290 \r
3291 /*\r
3292     Warning: this function modifies the behavior of several other functions. \r
3293     \r
3294     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3295     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3296     repaint is scattered all over the place, which is not good for features such as\r
3297     "arrow highlighting" that require a full repaint of the board.\r
3298 \r
3299     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3300     user interaction, when speed is not so important) but especially to avoid errors\r
3301     in the displayed graphics.\r
3302 \r
3303     In such patched places, I always try refer to this function so there is a single\r
3304     place to maintain knowledge.\r
3305     \r
3306     To restore the original behavior, just return FALSE unconditionally.\r
3307 */\r
3308 BOOL IsFullRepaintPreferrable()\r
3309 {\r
3310     BOOL result = FALSE;\r
3311 \r
3312     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3313         /* Arrow may appear on the board */\r
3314         result = TRUE;\r
3315     }\r
3316 \r
3317     return result;\r
3318 }\r
3319 \r
3320 /* \r
3321     This function is called by DrawPosition to know whether a full repaint must\r
3322     be forced or not.\r
3323 \r
3324     Only DrawPosition may directly call this function, which makes use of \r
3325     some state information. Other function should call DrawPosition specifying \r
3326     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3327 */\r
3328 BOOL DrawPositionNeedsFullRepaint()\r
3329 {\r
3330     BOOL result = FALSE;\r
3331 \r
3332     /* \r
3333         Probably a slightly better policy would be to trigger a full repaint\r
3334         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3335         but animation is fast enough that it's difficult to notice.\r
3336     */\r
3337     if( animInfo.piece == EmptySquare ) {\r
3338         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3339             result = TRUE;\r
3340         }\r
3341     }\r
3342 \r
3343     return result;\r
3344 }\r
3345 \r
3346 static HBITMAP borderBitmap;\r
3347 \r
3348 VOID\r
3349 DrawBackgroundOnDC(HDC hdc)\r
3350 {\r
3351   \r
3352   BITMAP bi;\r
3353   HDC tmphdc;\r
3354   HBITMAP hbm;\r
3355   static char oldBorder[MSG_SIZ];\r
3356   int w = 600, h = 600, mode;\r
3357 \r
3358   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3359     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3360     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3361   }\r
3362   if(borderBitmap == NULL) { // loading failed, use white\r
3363     FillRect( hdc, &boardRect, whitePieceBrush );\r
3364     return;\r
3365   }\r
3366   tmphdc = CreateCompatibleDC(hdc);\r
3367   hbm = SelectObject(tmphdc, borderBitmap);\r
3368   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3369             w = bi.bmWidth;\r
3370             h = bi.bmHeight;\r
3371   }\r
3372   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3373   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3374                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3375   SetStretchBltMode(hdc, mode);\r
3376   SelectObject(tmphdc, hbm);\r
3377   DeleteDC(tmphdc);\r
3378 }\r
3379 \r
3380 VOID\r
3381 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3382 {\r
3383   int row, column, x, y, square_color, piece_color;\r
3384   ChessSquare piece;\r
3385   HBRUSH oldBrush;\r
3386   HDC texture_hdc = NULL;\r
3387 \r
3388   /* [AS] Initialize background textures if needed */\r
3389   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3390       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3391       if( backTextureSquareSize != squareSize \r
3392        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3393           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3394           backTextureSquareSize = squareSize;\r
3395           RebuildTextureSquareInfo();\r
3396       }\r
3397 \r
3398       texture_hdc = CreateCompatibleDC( hdc );\r
3399   }\r
3400 \r
3401   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3402     for (column = 0; column < BOARD_WIDTH; column++) {\r
3403   \r
3404       SquareToPos(row, column, &x, &y);\r
3405 \r
3406       piece = board[row][column];\r
3407 \r
3408       square_color = ((column + row) % 2) == 1;\r
3409       if( gameInfo.variant == VariantXiangqi ) {\r
3410           square_color = !InPalace(row, column);\r
3411           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3412           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3413       }\r
3414       piece_color = (int) piece < (int) BlackPawn;\r
3415 \r
3416 \r
3417       /* [HGM] holdings file: light square or black */\r
3418       if(column == BOARD_LEFT-2) {\r
3419             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3420                 square_color = 1;\r
3421             else {\r
3422                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3423                 continue;\r
3424             }\r
3425       } else\r
3426       if(column == BOARD_RGHT + 1 ) {\r
3427             if( row < gameInfo.holdingsSize )\r
3428                 square_color = 1;\r
3429             else {\r
3430                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3431                 continue;\r
3432             }\r
3433       }\r
3434       if(column == BOARD_LEFT-1 ) /* left align */\r
3435             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3436       else if( column == BOARD_RGHT) /* right align */\r
3437             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3438       else\r
3439       if (appData.monoMode) {\r
3440         if (piece == EmptySquare) {\r
3441           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3442                  square_color ? WHITENESS : BLACKNESS);\r
3443         } else {\r
3444           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3445         }\r
3446       } \r
3447       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3448           /* [AS] Draw the square using a texture bitmap */\r
3449           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3450           int r = row, c = column; // [HGM] do not flip board in flipView\r
3451           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3452 \r
3453           DrawTile( x, y, \r
3454               squareSize, squareSize, \r
3455               hdc, \r
3456               texture_hdc,\r
3457               backTextureSquareInfo[r][c].mode,\r
3458               backTextureSquareInfo[r][c].x,\r
3459               backTextureSquareInfo[r][c].y );\r
3460 \r
3461           SelectObject( texture_hdc, hbm );\r
3462 \r
3463           if (piece != EmptySquare) {\r
3464               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3465           }\r
3466       }\r
3467       else {\r
3468         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3469 \r
3470         oldBrush = SelectObject(hdc, brush );\r
3471         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3472         SelectObject(hdc, oldBrush);\r
3473         if (piece != EmptySquare)\r
3474           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3475       }\r
3476     }\r
3477   }\r
3478 \r
3479   if( texture_hdc != NULL ) {\r
3480     DeleteDC( texture_hdc );\r
3481   }\r
3482 }\r
3483 \r
3484 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3485 void fputDW(FILE *f, int x)\r
3486 {\r
3487         fputc(x     & 255, f);\r
3488         fputc(x>>8  & 255, f);\r
3489         fputc(x>>16 & 255, f);\r
3490         fputc(x>>24 & 255, f);\r
3491 }\r
3492 \r
3493 #define MAX_CLIPS 200   /* more than enough */\r
3494 \r
3495 VOID\r
3496 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3497 {\r
3498 //  HBITMAP bufferBitmap;\r
3499   BITMAP bi;\r
3500 //  RECT Rect;\r
3501   HDC tmphdc;\r
3502   HBITMAP hbm;\r
3503   int w = 100, h = 50;\r
3504 \r
3505   if(logo == NULL) {\r
3506     if(!logoHeight) return;\r
3507     FillRect( hdc, &logoRect, whitePieceBrush );\r
3508   }\r
3509 //  GetClientRect(hwndMain, &Rect);\r
3510 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3511 //                                      Rect.bottom-Rect.top+1);\r
3512   tmphdc = CreateCompatibleDC(hdc);\r
3513   hbm = SelectObject(tmphdc, logo);\r
3514   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3515             w = bi.bmWidth;\r
3516             h = bi.bmHeight;\r
3517   }\r
3518   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3519                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3520   SelectObject(tmphdc, hbm);\r
3521   DeleteDC(tmphdc);\r
3522 }\r
3523 \r
3524 VOID\r
3525 DisplayLogos()\r
3526 {\r
3527   if(logoHeight) {\r
3528         HDC hdc = GetDC(hwndMain);\r
3529         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3530         if(appData.autoLogo) {\r
3531           \r
3532           switch(gameMode) { // pick logos based on game mode\r
3533             case IcsObserving:\r
3534                 whiteLogo = second.programLogo; // ICS logo\r
3535                 blackLogo = second.programLogo;\r
3536             default:\r
3537                 break;\r
3538             case IcsPlayingWhite:\r
3539                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3540                 blackLogo = second.programLogo; // ICS logo\r
3541                 break;\r
3542             case IcsPlayingBlack:\r
3543                 whiteLogo = second.programLogo; // ICS logo\r
3544                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3545                 break;\r
3546             case TwoMachinesPlay:\r
3547                 if(first.twoMachinesColor[0] == 'b') {\r
3548                     whiteLogo = second.programLogo;\r
3549                     blackLogo = first.programLogo;\r
3550                 }\r
3551                 break;\r
3552             case MachinePlaysWhite:\r
3553                 blackLogo = userLogo;\r
3554                 break;\r
3555             case MachinePlaysBlack:\r
3556                 whiteLogo = userLogo;\r
3557                 blackLogo = first.programLogo;\r
3558           }\r
3559         }\r
3560         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3561         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3562         ReleaseDC(hwndMain, hdc);\r
3563   }\r
3564 }\r
3565 \r
3566 void\r
3567 UpdateLogos(int display)\r
3568 { // called after loading new engine(s), in tourney or from menu\r
3569   LoadLogo(&first, 0, FALSE);\r
3570   LoadLogo(&second, 1, appData.icsActive);\r
3571   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3572   if(display) DisplayLogos();\r
3573 }\r
3574 \r
3575 static HDC hdcSeek;\r
3576 \r
3577 // [HGM] seekgraph\r
3578 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3579 {\r
3580     POINT stPt;\r
3581     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3582     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3583     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3584     SelectObject( hdcSeek, hp );\r
3585 }\r
3586 \r
3587 // front-end wrapper for drawing functions to do rectangles\r
3588 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3589 {\r
3590     HPEN hp;\r
3591     RECT rc;\r
3592 \r
3593     if (hdcSeek == NULL) {\r
3594     hdcSeek = GetDC(hwndMain);\r
3595       if (!appData.monoMode) {\r
3596         SelectPalette(hdcSeek, hPal, FALSE);\r
3597         RealizePalette(hdcSeek);\r
3598       }\r
3599     }\r
3600     hp = SelectObject( hdcSeek, gridPen );\r
3601     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3602     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3603     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3604     SelectObject( hdcSeek, hp );\r
3605 }\r
3606 \r
3607 // front-end wrapper for putting text in graph\r
3608 void DrawSeekText(char *buf, int x, int y)\r
3609 {\r
3610         SIZE stSize;\r
3611         SetBkMode( hdcSeek, TRANSPARENT );\r
3612         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3613         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3614 }\r
3615 \r
3616 void DrawSeekDot(int x, int y, int color)\r
3617 {\r
3618         int square = color & 0x80;\r
3619         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3620                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3621         color &= 0x7F;\r
3622         if(square)\r
3623             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3624                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3625         else\r
3626             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3627                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3628             SelectObject(hdcSeek, oldBrush);\r
3629 }\r
3630 \r
3631 void DrawSeekOpen()\r
3632 {\r
3633 }\r
3634 \r
3635 void DrawSeekClose()\r
3636 {\r
3637 }\r
3638 \r
3639 VOID\r
3640 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3641 {\r
3642   static Board lastReq[2], lastDrawn[2];\r
3643   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3644   static int lastDrawnFlipView = 0;\r
3645   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3646   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3647   HDC tmphdc;\r
3648   HDC hdcmem;\r
3649   HBITMAP bufferBitmap;\r
3650   HBITMAP oldBitmap;\r
3651   RECT Rect;\r
3652   HRGN clips[MAX_CLIPS];\r
3653   ChessSquare dragged_piece = EmptySquare;\r
3654   int nr = twoBoards*partnerUp;\r
3655 \r
3656   /* I'm undecided on this - this function figures out whether a full\r
3657    * repaint is necessary on its own, so there's no real reason to have the\r
3658    * caller tell it that.  I think this can safely be set to FALSE - but\r
3659    * if we trust the callers not to request full repaints unnessesarily, then\r
3660    * we could skip some clipping work.  In other words, only request a full\r
3661    * redraw when the majority of pieces have changed positions (ie. flip, \r
3662    * gamestart and similar)  --Hawk\r
3663    */\r
3664   Boolean fullrepaint = repaint;\r
3665 \r
3666   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3667 \r
3668   if( DrawPositionNeedsFullRepaint() ) {\r
3669       fullrepaint = TRUE;\r
3670   }\r
3671 \r
3672   if (board == NULL) {\r
3673     if (!lastReqValid[nr]) {\r
3674       return;\r
3675     }\r
3676     board = lastReq[nr];\r
3677   } else {\r
3678     CopyBoard(lastReq[nr], board);\r
3679     lastReqValid[nr] = 1;\r
3680   }\r
3681 \r
3682   if (doingSizing) {\r
3683     return;\r
3684   }\r
3685 \r
3686   if (IsIconic(hwndMain)) {\r
3687     return;\r
3688   }\r
3689 \r
3690   if (hdc == NULL) {\r
3691     hdc = GetDC(hwndMain);\r
3692     if (!appData.monoMode) {\r
3693       SelectPalette(hdc, hPal, FALSE);\r
3694       RealizePalette(hdc);\r
3695     }\r
3696     releaseDC = TRUE;\r
3697   } else {\r
3698     releaseDC = FALSE;\r
3699   }\r
3700 \r
3701   /* Create some work-DCs */\r
3702   hdcmem = CreateCompatibleDC(hdc);\r
3703   tmphdc = CreateCompatibleDC(hdc);\r
3704 \r
3705   /* If dragging is in progress, we temporarely remove the piece */\r
3706   /* [HGM] or temporarily decrease count if stacked              */\r
3707   /*       !! Moved to before board compare !!                   */\r
3708   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3709     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3710     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3711             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3712         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3713     } else \r
3714     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3715             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3716         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3717     } else \r
3718         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3719   }\r
3720 \r
3721   /* Figure out which squares need updating by comparing the \r
3722    * newest board with the last drawn board and checking if\r
3723    * flipping has changed.\r
3724    */\r
3725   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3726     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3727       for (column = 0; column < BOARD_WIDTH; column++) {\r
3728         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3729           SquareToPos(row, column, &x, &y);\r
3730           clips[num_clips++] =\r
3731             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3732         }\r
3733       }\r
3734     }\r
3735    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3736     for (i=0; i<2; i++) {\r
3737       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3738           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3739         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3740             lastDrawnHighlight.sq[i].y >= 0) {\r
3741           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3742                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3743           clips[num_clips++] =\r
3744             CreateRectRgn(x - lineGap, y - lineGap, \r
3745                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3746         }\r
3747         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3748           SquareToPos(highlightInfo.sq[i].y, highlightInfo.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       }\r
3754     }\r
3755     for (i=0; i<2; i++) {\r
3756       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3757           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3758         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3759             lastDrawnPremove.sq[i].y >= 0) {\r
3760           SquareToPos(lastDrawnPremove.sq[i].y,\r
3761                       lastDrawnPremove.sq[i].x, &x, &y);\r
3762           clips[num_clips++] =\r
3763             CreateRectRgn(x - lineGap, y - lineGap, \r
3764                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3765         }\r
3766         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3767             premoveHighlightInfo.sq[i].y >= 0) {\r
3768           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3769                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3770           clips[num_clips++] =\r
3771             CreateRectRgn(x - lineGap, y - lineGap, \r
3772                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3773         }\r
3774       }\r
3775     }\r
3776    } else { // nr == 1\r
3777         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3778         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3779         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3780         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3781       for (i=0; i<2; i++) {\r
3782         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3783             partnerHighlightInfo.sq[i].y >= 0) {\r
3784           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3785                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3786           clips[num_clips++] =\r
3787             CreateRectRgn(x - lineGap, y - lineGap, \r
3788                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3789         }\r
3790         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3791             oldPartnerHighlight.sq[i].y >= 0) {\r
3792           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3793                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3794           clips[num_clips++] =\r
3795             CreateRectRgn(x - lineGap, y - lineGap, \r
3796                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3797         }\r
3798       }\r
3799    }\r
3800   } else {\r
3801     fullrepaint = TRUE;\r
3802   }\r
3803 \r
3804   /* Create a buffer bitmap - this is the actual bitmap\r
3805    * being written to.  When all the work is done, we can\r
3806    * copy it to the real DC (the screen).  This avoids\r
3807    * the problems with flickering.\r
3808    */\r
3809   GetClientRect(hwndMain, &Rect);\r
3810   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3811                                         Rect.bottom-Rect.top+1);\r
3812   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3813   if (!appData.monoMode) {\r
3814     SelectPalette(hdcmem, hPal, FALSE);\r
3815   }\r
3816 \r
3817   /* Create clips for dragging */\r
3818   if (!fullrepaint) {\r
3819     if (dragInfo.from.x >= 0) {\r
3820       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3821       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3822     }\r
3823     if (dragInfo.start.x >= 0) {\r
3824       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3825       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3826     }\r
3827     if (dragInfo.pos.x >= 0) {\r
3828       x = dragInfo.pos.x - squareSize / 2;\r
3829       y = dragInfo.pos.y - squareSize / 2;\r
3830       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3831     }\r
3832     if (dragInfo.lastpos.x >= 0) {\r
3833       x = dragInfo.lastpos.x - squareSize / 2;\r
3834       y = dragInfo.lastpos.y - squareSize / 2;\r
3835       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3836     }\r
3837   }\r
3838 \r
3839   /* Are we animating a move?  \r
3840    * If so, \r
3841    *   - remove the piece from the board (temporarely)\r
3842    *   - calculate the clipping region\r
3843    */\r
3844   if (!fullrepaint) {\r
3845     if (animInfo.piece != EmptySquare) {\r
3846       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3847       x = boardRect.left + animInfo.lastpos.x;\r
3848       y = boardRect.top + animInfo.lastpos.y;\r
3849       x2 = boardRect.left + animInfo.pos.x;\r
3850       y2 = boardRect.top + animInfo.pos.y;\r
3851       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3852       /* Slight kludge.  The real problem is that after AnimateMove is\r
3853          done, the position on the screen does not match lastDrawn.\r
3854          This currently causes trouble only on e.p. captures in\r
3855          atomic, where the piece moves to an empty square and then\r
3856          explodes.  The old and new positions both had an empty square\r
3857          at the destination, but animation has drawn a piece there and\r
3858          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3859       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3860     }\r
3861   }\r
3862 \r
3863   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3864   if (num_clips == 0)\r
3865     fullrepaint = TRUE;\r
3866 \r
3867   /* Set clipping on the memory DC */\r
3868   if (!fullrepaint) {\r
3869     SelectClipRgn(hdcmem, clips[0]);\r
3870     for (x = 1; x < num_clips; x++) {\r
3871       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3872         abort();  // this should never ever happen!\r
3873     }\r
3874   }\r
3875 \r
3876   /* Do all the drawing to the memory DC */\r
3877   if(explodeInfo.radius) { // [HGM] atomic\r
3878         HBRUSH oldBrush;\r
3879         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3880         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3881         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3882         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3883         x += squareSize/2;\r
3884         y += squareSize/2;\r
3885         if(!fullrepaint) {\r
3886           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3887           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3888         }\r
3889         DrawGridOnDC(hdcmem);\r
3890         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3891         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3892         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3893         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3894         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3895         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3896         SelectObject(hdcmem, oldBrush);\r
3897   } else {\r
3898     if(border) DrawBackgroundOnDC(hdcmem);\r
3899     DrawGridOnDC(hdcmem);\r
3900     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3901         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3902         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3903     } else {\r
3904         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3905         oldPartnerHighlight = partnerHighlightInfo;\r
3906     }\r
3907     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3908   }\r
3909   if(nr == 0) // [HGM] dual: markers only on left board\r
3910   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3911     for (column = 0; column < BOARD_WIDTH; column++) {\r
3912         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3913             HBRUSH oldBrush = SelectObject(hdcmem, \r
3914                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3915             SquareToPos(row, column, &x, &y);\r
3916             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3917                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3918             SelectObject(hdcmem, oldBrush);\r
3919         }\r
3920     }\r
3921   }\r
3922 \r
3923   if( appData.highlightMoveWithArrow ) {\r
3924     DrawArrowHighlight(hdcmem);\r
3925   }\r
3926 \r
3927   DrawCoordsOnDC(hdcmem);\r
3928 \r
3929   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3930                  /* to make sure lastDrawn contains what is actually drawn */\r
3931 \r
3932   /* Put the dragged piece back into place and draw it (out of place!) */\r
3933     if (dragged_piece != EmptySquare) {\r
3934     /* [HGM] or restack */\r
3935     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3936                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3937     else\r
3938     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3939                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3940     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3941     x = dragInfo.pos.x - squareSize / 2;\r
3942     y = dragInfo.pos.y - squareSize / 2;\r
3943     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3944                   ((int) dragInfo.piece < (int) BlackPawn), \r
3945                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3946   }   \r
3947   \r
3948   /* Put the animated piece back into place and draw it */\r
3949   if (animInfo.piece != EmptySquare) {\r
3950     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3951     x = boardRect.left + animInfo.pos.x;\r
3952     y = boardRect.top + animInfo.pos.y;\r
3953     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3954                   ((int) animInfo.piece < (int) BlackPawn),\r
3955                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3956   }\r
3957 \r
3958   /* Release the bufferBitmap by selecting in the old bitmap \r
3959    * and delete the memory DC\r
3960    */\r
3961   SelectObject(hdcmem, oldBitmap);\r
3962   DeleteDC(hdcmem);\r
3963 \r
3964   /* Set clipping on the target DC */\r
3965   if (!fullrepaint) {\r
3966     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3967         RECT rect;\r
3968         GetRgnBox(clips[x], &rect);\r
3969         DeleteObject(clips[x]);\r
3970         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3971                           rect.right + wpMain.width/2, rect.bottom);\r
3972     }\r
3973     SelectClipRgn(hdc, clips[0]);\r
3974     for (x = 1; x < num_clips; x++) {\r
3975       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3976         abort();   // this should never ever happen!\r
3977     } \r
3978   }\r
3979 \r
3980   /* Copy the new bitmap onto the screen in one go.\r
3981    * This way we avoid any flickering\r
3982    */\r
3983   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3984   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3985          boardRect.right - boardRect.left,\r
3986          boardRect.bottom - boardRect.top,\r
3987          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3988   if(saveDiagFlag) { \r
3989     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3990     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3991 \r
3992     GetObject(bufferBitmap, sizeof(b), &b);\r
3993     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3994         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3995         bih.biWidth = b.bmWidth;\r
3996         bih.biHeight = b.bmHeight;\r
3997         bih.biPlanes = 1;\r
3998         bih.biBitCount = b.bmBitsPixel;\r
3999         bih.biCompression = 0;\r
4000         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4001         bih.biXPelsPerMeter = 0;\r
4002         bih.biYPelsPerMeter = 0;\r
4003         bih.biClrUsed = 0;\r
4004         bih.biClrImportant = 0;\r
4005 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4006 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4007         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4008 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4009 \r
4010         wb = b.bmWidthBytes;\r
4011         // count colors\r
4012         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4013                 int k = ((int*) pData)[i];\r
4014                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4015                 if(j >= 16) break;\r
4016                 color[j] = k;\r
4017                 if(j >= nrColors) nrColors = j+1;\r
4018         }\r
4019         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4020                 INT p = 0;\r
4021                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4022                     for(w=0; w<(wb>>2); w+=2) {\r
4023                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4024                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4025                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4026                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4027                         pData[p++] = m | j<<4;\r
4028                     }\r
4029                     while(p&3) pData[p++] = 0;\r
4030                 }\r
4031                 fac = 3;\r
4032                 wb = ((wb+31)>>5)<<2;\r
4033         }\r
4034         // write BITMAPFILEHEADER\r
4035         fprintf(diagFile, "BM");\r
4036         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4037         fputDW(diagFile, 0);\r
4038         fputDW(diagFile, 0x36 + (fac?64:0));\r
4039         // write BITMAPINFOHEADER\r
4040         fputDW(diagFile, 40);\r
4041         fputDW(diagFile, b.bmWidth);\r
4042         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4043         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4044         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4045         fputDW(diagFile, 0);\r
4046         fputDW(diagFile, 0);\r
4047         fputDW(diagFile, 0);\r
4048         fputDW(diagFile, 0);\r
4049         fputDW(diagFile, 0);\r
4050         fputDW(diagFile, 0);\r
4051         // write color table\r
4052         if(fac)\r
4053         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4054         // write bitmap data\r
4055         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4056                 fputc(pData[i], diagFile);\r
4057         free(pData);\r
4058      }\r
4059   }\r
4060 \r
4061   SelectObject(tmphdc, oldBitmap);\r
4062 \r
4063   /* Massive cleanup */\r
4064   for (x = 0; x < num_clips; x++)\r
4065     DeleteObject(clips[x]);\r
4066 \r
4067   DeleteDC(tmphdc);\r
4068   DeleteObject(bufferBitmap);\r
4069 \r
4070   if (releaseDC) \r
4071     ReleaseDC(hwndMain, hdc);\r
4072   \r
4073   if (lastDrawnFlipView != flipView && nr == 0) {\r
4074     if (flipView)\r
4075       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4076     else\r
4077       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4078   }\r
4079 \r
4080 /*  CopyBoard(lastDrawn, board);*/\r
4081   lastDrawnHighlight = highlightInfo;\r
4082   lastDrawnPremove   = premoveHighlightInfo;\r
4083   lastDrawnFlipView = flipView;\r
4084   lastDrawnValid[nr] = 1;\r
4085 }\r
4086 \r
4087 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4088 int\r
4089 SaveDiagram(f)\r
4090      FILE *f;\r
4091 {\r
4092     saveDiagFlag = 1; diagFile = f;\r
4093     HDCDrawPosition(NULL, TRUE, NULL);\r
4094     saveDiagFlag = 0;\r
4095 \r
4096     fclose(f);\r
4097     return TRUE;\r
4098 }\r
4099 \r
4100 \r
4101 /*---------------------------------------------------------------------------*\\r
4102 | CLIENT PAINT PROCEDURE\r
4103 |   This is the main event-handler for the WM_PAINT message.\r
4104 |\r
4105 \*---------------------------------------------------------------------------*/\r
4106 VOID\r
4107 PaintProc(HWND hwnd)\r
4108 {\r
4109   HDC         hdc;\r
4110   PAINTSTRUCT ps;\r
4111   HFONT       oldFont;\r
4112 \r
4113   if((hdc = BeginPaint(hwnd, &ps))) {\r
4114     if (IsIconic(hwnd)) {\r
4115       DrawIcon(hdc, 2, 2, iconCurrent);\r
4116     } else {\r
4117       if (!appData.monoMode) {\r
4118         SelectPalette(hdc, hPal, FALSE);\r
4119         RealizePalette(hdc);\r
4120       }\r
4121       HDCDrawPosition(hdc, 1, NULL);\r
4122       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4123         flipView = !flipView; partnerUp = !partnerUp;\r
4124         HDCDrawPosition(hdc, 1, NULL);\r
4125         flipView = !flipView; partnerUp = !partnerUp;\r
4126       }\r
4127       oldFont =\r
4128         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4129       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4130                  ETO_CLIPPED|ETO_OPAQUE,\r
4131                  &messageRect, messageText, strlen(messageText), NULL);\r
4132       SelectObject(hdc, oldFont);\r
4133       DisplayBothClocks();\r
4134       DisplayLogos();\r
4135     }\r
4136     EndPaint(hwnd,&ps);\r
4137   }\r
4138 \r
4139   return;\r
4140 }\r
4141 \r
4142 \r
4143 /*\r
4144  * If the user selects on a border boundary, return -1; if off the board,\r
4145  *   return -2.  Otherwise map the event coordinate to the square.\r
4146  * The offset boardRect.left or boardRect.top must already have been\r
4147  *   subtracted from x.\r
4148  */\r
4149 int EventToSquare(x, limit)\r
4150      int x, limit;\r
4151 {\r
4152   if (x <= border)\r
4153     return -2;\r
4154   if (x < lineGap + border)\r
4155     return -1;\r
4156   x -= lineGap + border;\r
4157   if ((x % (squareSize + lineGap)) >= squareSize)\r
4158     return -1;\r
4159   x /= (squareSize + lineGap);\r
4160     if (x >= limit)\r
4161     return -2;\r
4162   return x;\r
4163 }\r
4164 \r
4165 typedef struct {\r
4166   char piece;\r
4167   int command;\r
4168   char* name;\r
4169 } DropEnable;\r
4170 \r
4171 DropEnable dropEnables[] = {\r
4172   { 'P', DP_Pawn, N_("Pawn") },\r
4173   { 'N', DP_Knight, N_("Knight") },\r
4174   { 'B', DP_Bishop, N_("Bishop") },\r
4175   { 'R', DP_Rook, N_("Rook") },\r
4176   { 'Q', DP_Queen, N_("Queen") },\r
4177 };\r
4178 \r
4179 VOID\r
4180 SetupDropMenu(HMENU hmenu)\r
4181 {\r
4182   int i, count, enable;\r
4183   char *p;\r
4184   extern char white_holding[], black_holding[];\r
4185   char item[MSG_SIZ];\r
4186 \r
4187   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4188     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4189                dropEnables[i].piece);\r
4190     count = 0;\r
4191     while (p && *p++ == dropEnables[i].piece) count++;\r
4192       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4193     enable = count > 0 || !appData.testLegality\r
4194       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4195                       && !appData.icsActive);\r
4196     ModifyMenu(hmenu, dropEnables[i].command,\r
4197                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4198                dropEnables[i].command, item);\r
4199   }\r
4200 }\r
4201 \r
4202 void DragPieceBegin(int x, int y, Boolean instantly)\r
4203 {\r
4204       dragInfo.lastpos.x = boardRect.left + x;\r
4205       dragInfo.lastpos.y = boardRect.top + y;\r
4206       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4207       dragInfo.from.x = fromX;\r
4208       dragInfo.from.y = fromY;\r
4209       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4210       dragInfo.start = dragInfo.from;\r
4211       SetCapture(hwndMain);\r
4212 }\r
4213 \r
4214 void DragPieceEnd(int x, int y)\r
4215 {\r
4216     ReleaseCapture();\r
4217     dragInfo.start.x = dragInfo.start.y = -1;\r
4218     dragInfo.from = dragInfo.start;\r
4219     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4220 }\r
4221 \r
4222 void ChangeDragPiece(ChessSquare piece)\r
4223 {\r
4224     dragInfo.piece = piece;\r
4225 }\r
4226 \r
4227 /* Event handler for mouse messages */\r
4228 VOID\r
4229 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4230 {\r
4231   int x, y, menuNr;\r
4232   POINT pt;\r
4233   static int recursive = 0;\r
4234   HMENU hmenu;\r
4235   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4236 \r
4237   if (recursive) {\r
4238     if (message == WM_MBUTTONUP) {\r
4239       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4240          to the middle button: we simulate pressing the left button too!\r
4241          */\r
4242       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4243       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4244     }\r
4245     return;\r
4246   }\r
4247   recursive++;\r
4248   \r
4249   pt.x = LOWORD(lParam);\r
4250   pt.y = HIWORD(lParam);\r
4251   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4252   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4253   if (!flipView && y >= 0) {\r
4254     y = BOARD_HEIGHT - 1 - y;\r
4255   }\r
4256   if (flipView && x >= 0) {\r
4257     x = BOARD_WIDTH - 1 - x;\r
4258   }\r
4259 \r
4260   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4261   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4262 \r
4263   switch (message) {\r
4264   case WM_LBUTTONDOWN:\r
4265       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4266         ClockClick(flipClock); break;\r
4267       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4268         ClockClick(!flipClock); break;\r
4269       }\r
4270       dragInfo.start.x = dragInfo.start.y = -1;\r
4271       dragInfo.from = dragInfo.start;\r
4272     if(fromX == -1 && frozen) { // not sure where this is for\r
4273                 fromX = fromY = -1; \r
4274       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4275       break;\r
4276     }\r
4277       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4278       DrawPosition(TRUE, NULL);\r
4279     break;\r
4280 \r
4281   case WM_LBUTTONUP:\r
4282       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4283       DrawPosition(TRUE, NULL);\r
4284     break;\r
4285 \r
4286   case WM_MOUSEMOVE:\r
4287     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4288     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4289     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4290     if ((appData.animateDragging || appData.highlightDragging)\r
4291         && (wParam & MK_LBUTTON)\r
4292         && dragInfo.from.x >= 0) \r
4293     {\r
4294       BOOL full_repaint = FALSE;\r
4295 \r
4296       if (appData.animateDragging) {\r
4297         dragInfo.pos = pt;\r
4298       }\r
4299       if (appData.highlightDragging) {\r
4300         SetHighlights(fromX, fromY, x, y);\r
4301         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4302             full_repaint = TRUE;\r
4303         }\r
4304       }\r
4305       \r
4306       DrawPosition( full_repaint, NULL);\r
4307       \r
4308       dragInfo.lastpos = dragInfo.pos;\r
4309     }\r
4310     break;\r
4311 \r
4312   case WM_MOUSEWHEEL: // [DM]\r
4313     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4314        /* Mouse Wheel is being rolled forward\r
4315         * Play moves forward\r
4316         */\r
4317        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4318                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4319        /* Mouse Wheel is being rolled backward\r
4320         * Play moves backward\r
4321         */\r
4322        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4323                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4324     }\r
4325     break;\r
4326 \r
4327   case WM_MBUTTONUP:\r
4328   case WM_RBUTTONUP:\r
4329     ReleaseCapture();\r
4330     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4331     break;\r
4332  \r
4333   case WM_MBUTTONDOWN:\r
4334   case WM_RBUTTONDOWN:\r
4335     ErrorPopDown();\r
4336     ReleaseCapture();\r
4337     fromX = fromY = -1;\r
4338     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4339     dragInfo.start.x = dragInfo.start.y = -1;\r
4340     dragInfo.from = dragInfo.start;\r
4341     dragInfo.lastpos = dragInfo.pos;\r
4342     if (appData.highlightDragging) {\r
4343       ClearHighlights();\r
4344     }\r
4345     if(y == -2) {\r
4346       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4347       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4348           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4349       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4350           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4351       }\r
4352       break;\r
4353     }\r
4354     DrawPosition(TRUE, NULL);\r
4355 \r
4356     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4357     switch (menuNr) {\r
4358     case 0:\r
4359       if (message == WM_MBUTTONDOWN) {\r
4360         buttonCount = 3;  /* even if system didn't think so */\r
4361         if (wParam & MK_SHIFT) \r
4362           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4363         else\r
4364           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4365       } else { /* message == WM_RBUTTONDOWN */\r
4366         /* Just have one menu, on the right button.  Windows users don't\r
4367            think to try the middle one, and sometimes other software steals\r
4368            it, or it doesn't really exist. */\r
4369         if(gameInfo.variant != VariantShogi)\r
4370             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4371         else\r
4372             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4373       }\r
4374       break;\r
4375     case 2:\r
4376       SetCapture(hwndMain);\r
4377       break;\r
4378     case 1:\r
4379       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4380       SetupDropMenu(hmenu);\r
4381       MenuPopup(hwnd, pt, hmenu, -1);\r
4382     default:\r
4383       break;\r
4384     }\r
4385     break;\r
4386   }\r
4387 \r
4388   recursive--;\r
4389 }\r
4390 \r
4391 /* Preprocess messages for buttons in main window */\r
4392 LRESULT CALLBACK\r
4393 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4394 {\r
4395   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4396   int i, dir;\r
4397 \r
4398   for (i=0; i<N_BUTTONS; i++) {\r
4399     if (buttonDesc[i].id == id) break;\r
4400   }\r
4401   if (i == N_BUTTONS) return 0;\r
4402   switch (message) {\r
4403   case WM_KEYDOWN:\r
4404     switch (wParam) {\r
4405     case VK_LEFT:\r
4406     case VK_RIGHT:\r
4407       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4408       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4409       return TRUE;\r
4410     }\r
4411     break;\r
4412   case WM_CHAR:\r
4413     switch (wParam) {\r
4414     case '\r':\r
4415       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4416       return TRUE;\r
4417     default:\r
4418       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4419         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4420         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4421         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4422         SetFocus(h);\r
4423         SendMessage(h, WM_CHAR, wParam, lParam);\r
4424         return TRUE;\r
4425       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4426         TypeInEvent((char)wParam);\r
4427       }\r
4428       break;\r
4429     }\r
4430     break;\r
4431   }\r
4432   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4433 }\r
4434 \r
4435 /* Process messages for Promotion dialog box */\r
4436 LRESULT CALLBACK\r
4437 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4438 {\r
4439   char promoChar;\r
4440 \r
4441   switch (message) {\r
4442   case WM_INITDIALOG: /* message: initialize dialog box */\r
4443     /* Center the dialog over the application window */\r
4444     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4445     Translate(hDlg, DLG_PromotionKing);\r
4446     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4447       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4448        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4449        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4450                SW_SHOW : SW_HIDE);\r
4451     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4452     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4453        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4454          PieceToChar(WhiteAngel) != '~') ||\r
4455         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4456          PieceToChar(BlackAngel) != '~')   ) ?\r
4457                SW_SHOW : SW_HIDE);\r
4458     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4459        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4460          PieceToChar(WhiteMarshall) != '~') ||\r
4461         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4462          PieceToChar(BlackMarshall) != '~')   ) ?\r
4463                SW_SHOW : SW_HIDE);\r
4464     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4465     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4466        gameInfo.variant != VariantShogi ?\r
4467                SW_SHOW : SW_HIDE);\r
4468     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4469        gameInfo.variant != VariantShogi ?\r
4470                SW_SHOW : SW_HIDE);\r
4471     if(gameInfo.variant == VariantShogi) {\r
4472         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4473         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4474         SetWindowText(hDlg, "Promote?");\r
4475     }\r
4476     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4477        gameInfo.variant == VariantSuper ?\r
4478                SW_SHOW : SW_HIDE);\r
4479     return TRUE;\r
4480 \r
4481   case WM_COMMAND: /* message: received a command */\r
4482     switch (LOWORD(wParam)) {\r
4483     case IDCANCEL:\r
4484       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4485       ClearHighlights();\r
4486       DrawPosition(FALSE, NULL);\r
4487       return TRUE;\r
4488     case PB_King:\r
4489       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4490       break;\r
4491     case PB_Queen:\r
4492       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4493       break;\r
4494     case PB_Rook:\r
4495       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4496       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4497       break;\r
4498     case PB_Bishop:\r
4499       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4500       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4501       break;\r
4502     case PB_Chancellor:\r
4503       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4504       break;\r
4505     case PB_Archbishop:\r
4506       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4507       break;\r
4508     case PB_Knight:\r
4509       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4510       break;\r
4511     default:\r
4512       return FALSE;\r
4513     }\r
4514     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4515     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4516     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4517     fromX = fromY = -1;\r
4518     if (!appData.highlightLastMove) {\r
4519       ClearHighlights();\r
4520       DrawPosition(FALSE, NULL);\r
4521     }\r
4522     return TRUE;\r
4523   }\r
4524   return FALSE;\r
4525 }\r
4526 \r
4527 /* Pop up promotion dialog */\r
4528 VOID\r
4529 PromotionPopup(HWND hwnd)\r
4530 {\r
4531   FARPROC lpProc;\r
4532 \r
4533   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4534   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4535     hwnd, (DLGPROC)lpProc);\r
4536   FreeProcInstance(lpProc);\r
4537 }\r
4538 \r
4539 void\r
4540 PromotionPopUp()\r
4541 {\r
4542   DrawPosition(TRUE, NULL);\r
4543   PromotionPopup(hwndMain);\r
4544 }\r
4545 \r
4546 VOID\r
4547 LoadGameDialog(HWND hwnd, char* title)\r
4548 {\r
4549   UINT number = 0;\r
4550   FILE *f;\r
4551   char fileTitle[MSG_SIZ];\r
4552   f = OpenFileDialog(hwnd, "rb", "",\r
4553                      appData.oldSaveStyle ? "gam" : "pgn",\r
4554                      GAME_FILT,\r
4555                      title, &number, fileTitle, NULL);\r
4556   if (f != NULL) {\r
4557     cmailMsgLoaded = FALSE;\r
4558     if (number == 0) {\r
4559       int error = GameListBuild(f);\r
4560       if (error) {\r
4561         DisplayError(_("Cannot build game list"), error);\r
4562       } else if (!ListEmpty(&gameList) &&\r
4563                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4564         GameListPopUp(f, fileTitle);\r
4565         return;\r
4566       }\r
4567       GameListDestroy();\r
4568       number = 1;\r
4569     }\r
4570     LoadGame(f, number, fileTitle, FALSE);\r
4571   }\r
4572 }\r
4573 \r
4574 int get_term_width()\r
4575 {\r
4576     HDC hdc;\r
4577     TEXTMETRIC tm;\r
4578     RECT rc;\r
4579     HFONT hfont, hold_font;\r
4580     LOGFONT lf;\r
4581     HWND hText;\r
4582 \r
4583     if (hwndConsole)\r
4584         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4585     else\r
4586         return 79;\r
4587 \r
4588     // get the text metrics\r
4589     hdc = GetDC(hText);\r
4590     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4591     if (consoleCF.dwEffects & CFE_BOLD)\r
4592         lf.lfWeight = FW_BOLD;\r
4593     if (consoleCF.dwEffects & CFE_ITALIC)\r
4594         lf.lfItalic = TRUE;\r
4595     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4596         lf.lfStrikeOut = TRUE;\r
4597     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4598         lf.lfUnderline = TRUE;\r
4599     hfont = CreateFontIndirect(&lf);\r
4600     hold_font = SelectObject(hdc, hfont);\r
4601     GetTextMetrics(hdc, &tm);\r
4602     SelectObject(hdc, hold_font);\r
4603     DeleteObject(hfont);\r
4604     ReleaseDC(hText, hdc);\r
4605 \r
4606     // get the rectangle\r
4607     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4608 \r
4609     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4610 }\r
4611 \r
4612 void UpdateICSWidth(HWND hText)\r
4613 {\r
4614     LONG old_width, new_width;\r
4615 \r
4616     new_width = get_term_width(hText, FALSE);\r
4617     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4618     if (new_width != old_width)\r
4619     {\r
4620         ics_update_width(new_width);\r
4621         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4622     }\r
4623 }\r
4624 \r
4625 VOID\r
4626 ChangedConsoleFont()\r
4627 {\r
4628   CHARFORMAT cfmt;\r
4629   CHARRANGE tmpsel, sel;\r
4630   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4631   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4632   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4633   PARAFORMAT paraf;\r
4634 \r
4635   cfmt.cbSize = sizeof(CHARFORMAT);\r
4636   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4637     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4638                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4639   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4640    * size.  This was undocumented in the version of MSVC++ that I had\r
4641    * when I wrote the code, but is apparently documented now.\r
4642    */\r
4643   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4644   cfmt.bCharSet = f->lf.lfCharSet;\r
4645   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4646   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4647   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4648   /* Why are the following seemingly needed too? */\r
4649   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4650   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4651   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4652   tmpsel.cpMin = 0;\r
4653   tmpsel.cpMax = -1; /*999999?*/\r
4654   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4655   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4656   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4657    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4658    */\r
4659   paraf.cbSize = sizeof(paraf);\r
4660   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4661   paraf.dxStartIndent = 0;\r
4662   paraf.dxOffset = WRAP_INDENT;\r
4663   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4664   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4665   UpdateICSWidth(hText);\r
4666 }\r
4667 \r
4668 /*---------------------------------------------------------------------------*\\r
4669  *\r
4670  * Window Proc for main window\r
4671  *\r
4672 \*---------------------------------------------------------------------------*/\r
4673 \r
4674 /* Process messages for main window, etc. */\r
4675 LRESULT CALLBACK\r
4676 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4677 {\r
4678   FARPROC lpProc;\r
4679   int wmId, wmEvent;\r
4680   char *defName;\r
4681   FILE *f;\r
4682   UINT number;\r
4683   char fileTitle[MSG_SIZ];\r
4684   static SnapData sd;\r
4685   static int peek=0;\r
4686 \r
4687   switch (message) {\r
4688 \r
4689   case WM_PAINT: /* message: repaint portion of window */\r
4690     PaintProc(hwnd);\r
4691     break;\r
4692 \r
4693   case WM_ERASEBKGND:\r
4694     if (IsIconic(hwnd)) {\r
4695       /* Cheat; change the message */\r
4696       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4697     } else {\r
4698       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4699     }\r
4700     break;\r
4701 \r
4702   case WM_LBUTTONDOWN:\r
4703   case WM_MBUTTONDOWN:\r
4704   case WM_RBUTTONDOWN:\r
4705   case WM_LBUTTONUP:\r
4706   case WM_MBUTTONUP:\r
4707   case WM_RBUTTONUP:\r
4708   case WM_MOUSEMOVE:\r
4709   case WM_MOUSEWHEEL:\r
4710     MouseEvent(hwnd, message, wParam, lParam);\r
4711     break;\r
4712 \r
4713   case WM_KEYUP:\r
4714     if((char)wParam == '\b') {\r
4715       ForwardEvent(); peek = 0;\r
4716     }\r
4717 \r
4718     JAWS_KBUP_NAVIGATION\r
4719 \r
4720     break;\r
4721 \r
4722   case WM_KEYDOWN:\r
4723     if((char)wParam == '\b') {\r
4724       if(!peek) BackwardEvent(), peek = 1;\r
4725     }\r
4726 \r
4727     JAWS_KBDOWN_NAVIGATION\r
4728 \r
4729     break;\r
4730 \r
4731   case WM_CHAR:\r
4732     \r
4733     JAWS_ALT_INTERCEPT\r
4734 \r
4735     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4736         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4737         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4738         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4739         SetFocus(h);\r
4740         SendMessage(h, message, wParam, lParam);\r
4741     } else if(lParam != KF_REPEAT) {\r
4742         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4743                 TypeInEvent((char)wParam);\r
4744         } else if((char)wParam == 003) CopyGameToClipboard();\r
4745          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4746     }\r
4747 \r
4748     break;\r
4749 \r
4750   case WM_PALETTECHANGED:\r
4751     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4752       int nnew;\r
4753       HDC hdc = GetDC(hwndMain);\r
4754       SelectPalette(hdc, hPal, TRUE);\r
4755       nnew = RealizePalette(hdc);\r
4756       if (nnew > 0) {\r
4757         paletteChanged = TRUE;\r
4758         InvalidateRect(hwnd, &boardRect, FALSE);\r
4759       }\r
4760       ReleaseDC(hwnd, hdc);\r
4761     }\r
4762     break;\r
4763 \r
4764   case WM_QUERYNEWPALETTE:\r
4765     if (!appData.monoMode /*&& paletteChanged*/) {\r
4766       int nnew;\r
4767       HDC hdc = GetDC(hwndMain);\r
4768       paletteChanged = FALSE;\r
4769       SelectPalette(hdc, hPal, FALSE);\r
4770       nnew = RealizePalette(hdc);\r
4771       if (nnew > 0) {\r
4772         InvalidateRect(hwnd, &boardRect, FALSE);\r
4773       }\r
4774       ReleaseDC(hwnd, hdc);\r
4775       return TRUE;\r
4776     }\r
4777     return FALSE;\r
4778 \r
4779   case WM_COMMAND: /* message: command from application menu */\r
4780     wmId    = LOWORD(wParam);\r
4781     wmEvent = HIWORD(wParam);\r
4782 \r
4783     switch (wmId) {\r
4784     case IDM_NewGame:\r
4785       ResetGameEvent();\r
4786       SAY("new game enter a move to play against the computer with white");\r
4787       break;\r
4788 \r
4789     case IDM_NewGameFRC:\r
4790       if( NewGameFRC() == 0 ) {\r
4791         ResetGameEvent();\r
4792       }\r
4793       break;\r
4794 \r
4795     case IDM_NewVariant:\r
4796       NewVariantPopup(hwnd);\r
4797       break;\r
4798 \r
4799     case IDM_LoadGame:\r
4800       LoadGameDialog(hwnd, _("Load Game from File"));\r
4801       break;\r
4802 \r
4803     case IDM_LoadNextGame:\r
4804       ReloadGame(1);\r
4805       break;\r
4806 \r
4807     case IDM_LoadPrevGame:\r
4808       ReloadGame(-1);\r
4809       break;\r
4810 \r
4811     case IDM_ReloadGame:\r
4812       ReloadGame(0);\r
4813       break;\r
4814 \r
4815     case IDM_LoadPosition:\r
4816       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4817         Reset(FALSE, TRUE);\r
4818       }\r
4819       number = 1;\r
4820       f = OpenFileDialog(hwnd, "rb", "",\r
4821                          appData.oldSaveStyle ? "pos" : "fen",\r
4822                          POSITION_FILT,\r
4823                          _("Load Position from File"), &number, fileTitle, NULL);\r
4824       if (f != NULL) {\r
4825         LoadPosition(f, number, fileTitle);\r
4826       }\r
4827       break;\r
4828 \r
4829     case IDM_LoadNextPosition:\r
4830       ReloadPosition(1);\r
4831       break;\r
4832 \r
4833     case IDM_LoadPrevPosition:\r
4834       ReloadPosition(-1);\r
4835       break;\r
4836 \r
4837     case IDM_ReloadPosition:\r
4838       ReloadPosition(0);\r
4839       break;\r
4840 \r
4841     case IDM_SaveGame:\r
4842       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4843       f = OpenFileDialog(hwnd, "a", defName,\r
4844                          appData.oldSaveStyle ? "gam" : "pgn",\r
4845                          GAME_FILT,\r
4846                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4847       if (f != NULL) {\r
4848         SaveGame(f, 0, "");\r
4849       }\r
4850       break;\r
4851 \r
4852     case IDM_SavePosition:\r
4853       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4854       f = OpenFileDialog(hwnd, "a", defName,\r
4855                          appData.oldSaveStyle ? "pos" : "fen",\r
4856                          POSITION_FILT,\r
4857                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4858       if (f != NULL) {\r
4859         SavePosition(f, 0, "");\r
4860       }\r
4861       break;\r
4862 \r
4863     case IDM_SaveDiagram:\r
4864       defName = "diagram";\r
4865       f = OpenFileDialog(hwnd, "wb", defName,\r
4866                          "bmp",\r
4867                          DIAGRAM_FILT,\r
4868                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4869       if (f != NULL) {\r
4870         SaveDiagram(f);\r
4871       }\r
4872       break;\r
4873 \r
4874     case IDM_CreateBook:\r
4875       CreateBookEvent();\r
4876       break;\r
4877 \r
4878     case IDM_CopyGame:\r
4879       CopyGameToClipboard();\r
4880       break;\r
4881 \r
4882     case IDM_PasteGame:\r
4883       PasteGameFromClipboard();\r
4884       break;\r
4885 \r
4886     case IDM_CopyGameListToClipboard:\r
4887       CopyGameListToClipboard();\r
4888       break;\r
4889 \r
4890     /* [AS] Autodetect FEN or PGN data */\r
4891     case IDM_PasteAny:\r
4892       PasteGameOrFENFromClipboard();\r
4893       break;\r
4894 \r
4895     /* [AS] Move history */\r
4896     case IDM_ShowMoveHistory:\r
4897         if( MoveHistoryIsUp() ) {\r
4898             MoveHistoryPopDown();\r
4899         }\r
4900         else {\r
4901             MoveHistoryPopUp();\r
4902         }\r
4903         break;\r
4904 \r
4905     /* [AS] Eval graph */\r
4906     case IDM_ShowEvalGraph:\r
4907         if( EvalGraphIsUp() ) {\r
4908             EvalGraphPopDown();\r
4909         }\r
4910         else {\r
4911             EvalGraphPopUp();\r
4912             SetFocus(hwndMain);\r
4913         }\r
4914         break;\r
4915 \r
4916     /* [AS] Engine output */\r
4917     case IDM_ShowEngineOutput:\r
4918         if( EngineOutputIsUp() ) {\r
4919             EngineOutputPopDown();\r
4920         }\r
4921         else {\r
4922             EngineOutputPopUp();\r
4923         }\r
4924         break;\r
4925 \r
4926     /* [AS] User adjudication */\r
4927     case IDM_UserAdjudication_White:\r
4928         UserAdjudicationEvent( +1 );\r
4929         break;\r
4930 \r
4931     case IDM_UserAdjudication_Black:\r
4932         UserAdjudicationEvent( -1 );\r
4933         break;\r
4934 \r
4935     case IDM_UserAdjudication_Draw:\r
4936         UserAdjudicationEvent( 0 );\r
4937         break;\r
4938 \r
4939     /* [AS] Game list options dialog */\r
4940     case IDM_GameListOptions:\r
4941       GameListOptions();\r
4942       break;\r
4943 \r
4944     case IDM_NewChat:\r
4945       ChatPopUp(NULL);\r
4946       break;\r
4947 \r
4948     case IDM_CopyPosition:\r
4949       CopyFENToClipboard();\r
4950       break;\r
4951 \r
4952     case IDM_PastePosition:\r
4953       PasteFENFromClipboard();\r
4954       break;\r
4955 \r
4956     case IDM_MailMove:\r
4957       MailMoveEvent();\r
4958       break;\r
4959 \r
4960     case IDM_ReloadCMailMsg:\r
4961       Reset(TRUE, TRUE);\r
4962       ReloadCmailMsgEvent(FALSE);\r
4963       break;\r
4964 \r
4965     case IDM_Minimize:\r
4966       ShowWindow(hwnd, SW_MINIMIZE);\r
4967       break;\r
4968 \r
4969     case IDM_Exit:\r
4970       ExitEvent(0);\r
4971       break;\r
4972 \r
4973     case IDM_MachineWhite:\r
4974       MachineWhiteEvent();\r
4975       /*\r
4976        * refresh the tags dialog only if it's visible\r
4977        */\r
4978       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4979           char *tags;\r
4980           tags = PGNTags(&gameInfo);\r
4981           TagsPopUp(tags, CmailMsg());\r
4982           free(tags);\r
4983       }\r
4984       SAY("computer starts playing white");\r
4985       break;\r
4986 \r
4987     case IDM_MachineBlack:\r
4988       MachineBlackEvent();\r
4989       /*\r
4990        * refresh the tags dialog only if it's visible\r
4991        */\r
4992       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4993           char *tags;\r
4994           tags = PGNTags(&gameInfo);\r
4995           TagsPopUp(tags, CmailMsg());\r
4996           free(tags);\r
4997       }\r
4998       SAY("computer starts playing black");\r
4999       break;\r
5000 \r
5001     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5002       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5003       break;\r
5004 \r
5005     case IDM_TwoMachines:\r
5006       TwoMachinesEvent();\r
5007       /*\r
5008        * refresh the tags dialog only if it's visible\r
5009        */\r
5010       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5011           char *tags;\r
5012           tags = PGNTags(&gameInfo);\r
5013           TagsPopUp(tags, CmailMsg());\r
5014           free(tags);\r
5015       }\r
5016       SAY("computer starts playing both sides");\r
5017       break;\r
5018 \r
5019     case IDM_AnalysisMode:\r
5020       if(AnalyzeModeEvent()) {\r
5021         SAY("analyzing current position");\r
5022       }\r
5023       break;\r
5024 \r
5025     case IDM_AnalyzeFile:\r
5026       AnalyzeFileEvent();\r
5027       break;\r
5028 \r
5029     case IDM_IcsClient:\r
5030       IcsClientEvent();\r
5031       break;\r
5032 \r
5033     case IDM_EditGame:\r
5034     case IDM_EditGame2:\r
5035       EditGameEvent();\r
5036       SAY("edit game");\r
5037       break;\r
5038 \r
5039     case IDM_EditPosition:\r
5040     case IDM_EditPosition2:\r
5041       EditPositionEvent();\r
5042       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5043       break;\r
5044 \r
5045     case IDM_Training:\r
5046       TrainingEvent();\r
5047       break;\r
5048 \r
5049     case IDM_ShowGameList:\r
5050       ShowGameListProc();\r
5051       break;\r
5052 \r
5053     case IDM_EditProgs1:\r
5054       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5055       break;\r
5056 \r
5057     case IDM_LoadProg1:\r
5058      LoadEnginePopUp(hwndMain, 0);\r
5059       break;\r
5060 \r
5061     case IDM_LoadProg2:\r
5062      LoadEnginePopUp(hwndMain, 1);\r
5063       break;\r
5064 \r
5065     case IDM_EditServers:\r
5066       EditTagsPopUp(icsNames, &icsNames);\r
5067       break;\r
5068 \r
5069     case IDM_EditTags:\r
5070     case IDM_Tags:\r
5071       EditTagsProc();\r
5072       break;\r
5073 \r
5074     case IDM_EditBook:\r
5075       EditBookEvent();\r
5076       break;\r
5077 \r
5078     case IDM_EditComment:\r
5079     case IDM_Comment:\r
5080       if (commentUp && editComment) {\r
5081         CommentPopDown();\r
5082       } else {\r
5083         EditCommentEvent();\r
5084       }\r
5085       break;\r
5086 \r
5087     case IDM_Pause:\r
5088       PauseEvent();\r
5089       break;\r
5090 \r
5091     case IDM_Accept:\r
5092       AcceptEvent();\r
5093       break;\r
5094 \r
5095     case IDM_Decline:\r
5096       DeclineEvent();\r
5097       break;\r
5098 \r
5099     case IDM_Rematch:\r
5100       RematchEvent();\r
5101       break;\r
5102 \r
5103     case IDM_CallFlag:\r
5104       CallFlagEvent();\r
5105       break;\r
5106 \r
5107     case IDM_Draw:\r
5108       DrawEvent();\r
5109       break;\r
5110 \r
5111     case IDM_Adjourn:\r
5112       AdjournEvent();\r
5113       break;\r
5114 \r
5115     case IDM_Abort:\r
5116       AbortEvent();\r
5117       break;\r
5118 \r
5119     case IDM_Resign:\r
5120       ResignEvent();\r
5121       break;\r
5122 \r
5123     case IDM_StopObserving:\r
5124       StopObservingEvent();\r
5125       break;\r
5126 \r
5127     case IDM_StopExamining:\r
5128       StopExaminingEvent();\r
5129       break;\r
5130 \r
5131     case IDM_Upload:\r
5132       UploadGameEvent();\r
5133       break;\r
5134 \r
5135     case IDM_TypeInMove:\r
5136       TypeInEvent('\000');\r
5137       break;\r
5138 \r
5139     case IDM_TypeInName:\r
5140       PopUpNameDialog('\000');\r
5141       break;\r
5142 \r
5143     case IDM_Backward:\r
5144       BackwardEvent();\r
5145       SetFocus(hwndMain);\r
5146       break;\r
5147 \r
5148     JAWS_MENU_ITEMS\r
5149 \r
5150     case IDM_Forward:\r
5151       ForwardEvent();\r
5152       SetFocus(hwndMain);\r
5153       break;\r
5154 \r
5155     case IDM_ToStart:\r
5156       ToStartEvent();\r
5157       SetFocus(hwndMain);\r
5158       break;\r
5159 \r
5160     case IDM_ToEnd:\r
5161       ToEndEvent();\r
5162       SetFocus(hwndMain);\r
5163       break;\r
5164 \r
5165     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5166     case OPT_GameListPrev:\r
5167       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5168       break;\r
5169 \r
5170     case IDM_Revert:\r
5171       RevertEvent(FALSE);\r
5172       break;\r
5173 \r
5174     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5175       RevertEvent(TRUE);\r
5176       break;\r
5177 \r
5178     case IDM_TruncateGame:\r
5179       TruncateGameEvent();\r
5180       break;\r
5181 \r
5182     case IDM_MoveNow:\r
5183       MoveNowEvent();\r
5184       break;\r
5185 \r
5186     case IDM_RetractMove:\r
5187       RetractMoveEvent();\r
5188       break;\r
5189 \r
5190     case IDM_FlipView:\r
5191       flipView = !flipView;\r
5192       DrawPosition(FALSE, NULL);\r
5193       break;\r
5194 \r
5195     case IDM_FlipClock:\r
5196       flipClock = !flipClock;\r
5197       DisplayBothClocks();\r
5198       DisplayLogos();\r
5199       break;\r
5200 \r
5201     case IDM_MuteSounds:\r
5202       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5203       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5204                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5205       break;\r
5206 \r
5207     case IDM_GeneralOptions:\r
5208       GeneralOptionsPopup(hwnd);\r
5209       DrawPosition(TRUE, NULL);\r
5210       break;\r
5211 \r
5212     case IDM_BoardOptions:\r
5213       BoardOptionsPopup(hwnd);\r
5214       break;\r
5215 \r
5216     case IDM_ThemeOptions:\r
5217       ThemeOptionsPopup(hwnd);\r
5218       break;\r
5219 \r
5220     case IDM_EnginePlayOptions:\r
5221       EnginePlayOptionsPopup(hwnd);\r
5222       break;\r
5223 \r
5224     case IDM_Engine1Options:\r
5225       EngineOptionsPopup(hwnd, &first);\r
5226       break;\r
5227 \r
5228     case IDM_Engine2Options:\r
5229       savedHwnd = hwnd;\r
5230       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5231       EngineOptionsPopup(hwnd, &second);\r
5232       break;\r
5233 \r
5234     case IDM_OptionsUCI:\r
5235       UciOptionsPopup(hwnd);\r
5236       break;\r
5237 \r
5238     case IDM_Tourney:\r
5239       TourneyPopup(hwnd);\r
5240       break;\r
5241 \r
5242     case IDM_IcsOptions:\r
5243       IcsOptionsPopup(hwnd);\r
5244       break;\r
5245 \r
5246     case IDM_Fonts:\r
5247       FontsOptionsPopup(hwnd);\r
5248       break;\r
5249 \r
5250     case IDM_Sounds:\r
5251       SoundOptionsPopup(hwnd);\r
5252       break;\r
5253 \r
5254     case IDM_CommPort:\r
5255       CommPortOptionsPopup(hwnd);\r
5256       break;\r
5257 \r
5258     case IDM_LoadOptions:\r
5259       LoadOptionsPopup(hwnd);\r
5260       break;\r
5261 \r
5262     case IDM_SaveOptions:\r
5263       SaveOptionsPopup(hwnd);\r
5264       break;\r
5265 \r
5266     case IDM_TimeControl:\r
5267       TimeControlOptionsPopup(hwnd);\r
5268       break;\r
5269 \r
5270     case IDM_SaveSettings:\r
5271       SaveSettings(settingsFileName);\r
5272       break;\r
5273 \r
5274     case IDM_SaveSettingsOnExit:\r
5275       saveSettingsOnExit = !saveSettingsOnExit;\r
5276       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5277                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5278                                          MF_CHECKED : MF_UNCHECKED));\r
5279       break;\r
5280 \r
5281     case IDM_Hint:\r
5282       HintEvent();\r
5283       break;\r
5284 \r
5285     case IDM_Book:\r
5286       BookEvent();\r
5287       break;\r
5288 \r
5289     case IDM_AboutGame:\r
5290       AboutGameEvent();\r
5291       break;\r
5292 \r
5293     case IDM_Debug:\r
5294       appData.debugMode = !appData.debugMode;\r
5295       if (appData.debugMode) {\r
5296         char dir[MSG_SIZ];\r
5297         GetCurrentDirectory(MSG_SIZ, dir);\r
5298         SetCurrentDirectory(installDir);\r
5299         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5300         SetCurrentDirectory(dir);\r
5301         setbuf(debugFP, NULL);\r
5302       } else {\r
5303         fclose(debugFP);\r
5304         debugFP = NULL;\r
5305       }\r
5306       break;\r
5307 \r
5308     case IDM_HELPCONTENTS:\r
5309       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5310           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5311           MessageBox (GetFocus(),\r
5312                     _("Unable to activate help"),\r
5313                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5314       }\r
5315       break;\r
5316 \r
5317     case IDM_HELPSEARCH:\r
5318         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5319             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5320         MessageBox (GetFocus(),\r
5321                     _("Unable to activate help"),\r
5322                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5323       }\r
5324       break;\r
5325 \r
5326     case IDM_HELPHELP:\r
5327       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5328         MessageBox (GetFocus(),\r
5329                     _("Unable to activate help"),\r
5330                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5331       }\r
5332       break;\r
5333 \r
5334     case IDM_ABOUT:\r
5335       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5336       DialogBox(hInst, \r
5337         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5338         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5339       FreeProcInstance(lpProc);\r
5340       break;\r
5341 \r
5342     case IDM_DirectCommand1:\r
5343       AskQuestionEvent(_("Direct Command"),\r
5344                        _("Send to chess program:"), "", "1");\r
5345       break;\r
5346     case IDM_DirectCommand2:\r
5347       AskQuestionEvent(_("Direct Command"),\r
5348                        _("Send to second chess program:"), "", "2");\r
5349       break;\r
5350 \r
5351     case EP_WhitePawn:\r
5352       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5353       fromX = fromY = -1;\r
5354       break;\r
5355 \r
5356     case EP_WhiteKnight:\r
5357       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5358       fromX = fromY = -1;\r
5359       break;\r
5360 \r
5361     case EP_WhiteBishop:\r
5362       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5363       fromX = fromY = -1;\r
5364       break;\r
5365 \r
5366     case EP_WhiteRook:\r
5367       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5368       fromX = fromY = -1;\r
5369       break;\r
5370 \r
5371     case EP_WhiteQueen:\r
5372       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5373       fromX = fromY = -1;\r
5374       break;\r
5375 \r
5376     case EP_WhiteFerz:\r
5377       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5378       fromX = fromY = -1;\r
5379       break;\r
5380 \r
5381     case EP_WhiteWazir:\r
5382       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5383       fromX = fromY = -1;\r
5384       break;\r
5385 \r
5386     case EP_WhiteAlfil:\r
5387       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5388       fromX = fromY = -1;\r
5389       break;\r
5390 \r
5391     case EP_WhiteCannon:\r
5392       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5393       fromX = fromY = -1;\r
5394       break;\r
5395 \r
5396     case EP_WhiteCardinal:\r
5397       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5398       fromX = fromY = -1;\r
5399       break;\r
5400 \r
5401     case EP_WhiteMarshall:\r
5402       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5403       fromX = fromY = -1;\r
5404       break;\r
5405 \r
5406     case EP_WhiteKing:\r
5407       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5408       fromX = fromY = -1;\r
5409       break;\r
5410 \r
5411     case EP_BlackPawn:\r
5412       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5413       fromX = fromY = -1;\r
5414       break;\r
5415 \r
5416     case EP_BlackKnight:\r
5417       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5418       fromX = fromY = -1;\r
5419       break;\r
5420 \r
5421     case EP_BlackBishop:\r
5422       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5423       fromX = fromY = -1;\r
5424       break;\r
5425 \r
5426     case EP_BlackRook:\r
5427       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5428       fromX = fromY = -1;\r
5429       break;\r
5430 \r
5431     case EP_BlackQueen:\r
5432       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5433       fromX = fromY = -1;\r
5434       break;\r
5435 \r
5436     case EP_BlackFerz:\r
5437       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5438       fromX = fromY = -1;\r
5439       break;\r
5440 \r
5441     case EP_BlackWazir:\r
5442       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5443       fromX = fromY = -1;\r
5444       break;\r
5445 \r
5446     case EP_BlackAlfil:\r
5447       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5448       fromX = fromY = -1;\r
5449       break;\r
5450 \r
5451     case EP_BlackCannon:\r
5452       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5453       fromX = fromY = -1;\r
5454       break;\r
5455 \r
5456     case EP_BlackCardinal:\r
5457       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5458       fromX = fromY = -1;\r
5459       break;\r
5460 \r
5461     case EP_BlackMarshall:\r
5462       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5463       fromX = fromY = -1;\r
5464       break;\r
5465 \r
5466     case EP_BlackKing:\r
5467       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5468       fromX = fromY = -1;\r
5469       break;\r
5470 \r
5471     case EP_EmptySquare:\r
5472       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5473       fromX = fromY = -1;\r
5474       break;\r
5475 \r
5476     case EP_ClearBoard:\r
5477       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5478       fromX = fromY = -1;\r
5479       break;\r
5480 \r
5481     case EP_White:\r
5482       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5483       fromX = fromY = -1;\r
5484       break;\r
5485 \r
5486     case EP_Black:\r
5487       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5488       fromX = fromY = -1;\r
5489       break;\r
5490 \r
5491     case EP_Promote:\r
5492       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5493       fromX = fromY = -1;\r
5494       break;\r
5495 \r
5496     case EP_Demote:\r
5497       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5498       fromX = fromY = -1;\r
5499       break;\r
5500 \r
5501     case DP_Pawn:\r
5502       DropMenuEvent(WhitePawn, fromX, fromY);\r
5503       fromX = fromY = -1;\r
5504       break;\r
5505 \r
5506     case DP_Knight:\r
5507       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5508       fromX = fromY = -1;\r
5509       break;\r
5510 \r
5511     case DP_Bishop:\r
5512       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5513       fromX = fromY = -1;\r
5514       break;\r
5515 \r
5516     case DP_Rook:\r
5517       DropMenuEvent(WhiteRook, fromX, fromY);\r
5518       fromX = fromY = -1;\r
5519       break;\r
5520 \r
5521     case DP_Queen:\r
5522       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5523       fromX = fromY = -1;\r
5524       break;\r
5525 \r
5526     case IDM_English:\r
5527       barbaric = 0; appData.language = "";\r
5528       TranslateMenus(0);\r
5529       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5530       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5531       lastChecked = wmId;\r
5532       break;\r
5533 \r
5534     default:\r
5535       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5536           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5537       else\r
5538       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5539           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5540           TranslateMenus(0);\r
5541           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5542           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5543           lastChecked = wmId;\r
5544           break;\r
5545       }\r
5546       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5547     }\r
5548     break;\r
5549 \r
5550   case WM_TIMER:\r
5551     switch (wParam) {\r
5552     case CLOCK_TIMER_ID:\r
5553       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5554       clockTimerEvent = 0;\r
5555       DecrementClocks(); /* call into back end */\r
5556       break;\r
5557     case LOAD_GAME_TIMER_ID:\r
5558       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5559       loadGameTimerEvent = 0;\r
5560       AutoPlayGameLoop(); /* call into back end */\r
5561       break;\r
5562     case ANALYSIS_TIMER_ID:\r
5563       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5564                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5565         AnalysisPeriodicEvent(0);\r
5566       } else {\r
5567         KillTimer(hwnd, analysisTimerEvent);\r
5568         analysisTimerEvent = 0;\r
5569       }\r
5570       break;\r
5571     case DELAYED_TIMER_ID:\r
5572       KillTimer(hwnd, delayedTimerEvent);\r
5573       delayedTimerEvent = 0;\r
5574       delayedTimerCallback();\r
5575       break;\r
5576     }\r
5577     break;\r
5578 \r
5579   case WM_USER_Input:\r
5580     InputEvent(hwnd, message, wParam, lParam);\r
5581     break;\r
5582 \r
5583   /* [AS] Also move "attached" child windows */\r
5584   case WM_WINDOWPOSCHANGING:\r
5585 \r
5586     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5587         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5588 \r
5589         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5590             /* Window is moving */\r
5591             RECT rcMain;\r
5592 \r
5593 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5594             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5595             rcMain.right  = wpMain.x + wpMain.width;\r
5596             rcMain.top    = wpMain.y;\r
5597             rcMain.bottom = wpMain.y + wpMain.height;\r
5598             \r
5599             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5600             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5601             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5602             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5603             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5604             wpMain.x = lpwp->x;\r
5605             wpMain.y = lpwp->y;\r
5606         }\r
5607     }\r
5608     break;\r
5609 \r
5610   /* [AS] Snapping */\r
5611   case WM_ENTERSIZEMOVE:\r
5612     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5613     if (hwnd == hwndMain) {\r
5614       doingSizing = TRUE;\r
5615       lastSizing = 0;\r
5616     }\r
5617     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5618     break;\r
5619 \r
5620   case WM_SIZING:\r
5621     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5622     if (hwnd == hwndMain) {\r
5623       lastSizing = wParam;\r
5624     }\r
5625     break;\r
5626 \r
5627   case WM_MOVING:\r
5628     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5629       return OnMoving( &sd, hwnd, wParam, lParam );\r
5630 \r
5631   case WM_EXITSIZEMOVE:\r
5632     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5633     if (hwnd == hwndMain) {\r
5634       RECT client;\r
5635       doingSizing = FALSE;\r
5636       InvalidateRect(hwnd, &boardRect, FALSE);\r
5637       GetClientRect(hwnd, &client);\r
5638       ResizeBoard(client.right, client.bottom, lastSizing);\r
5639       lastSizing = 0;\r
5640       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5641     }\r
5642     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5643     break;\r
5644 \r
5645   case WM_DESTROY: /* message: window being destroyed */\r
5646     PostQuitMessage(0);\r
5647     break;\r
5648 \r
5649   case WM_CLOSE:\r
5650     if (hwnd == hwndMain) {\r
5651       ExitEvent(0);\r
5652     }\r
5653     break;\r
5654 \r
5655   default:      /* Passes it on if unprocessed */\r
5656     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5657   }\r
5658   return 0;\r
5659 }\r
5660 \r
5661 /*---------------------------------------------------------------------------*\\r
5662  *\r
5663  * Misc utility routines\r
5664  *\r
5665 \*---------------------------------------------------------------------------*/\r
5666 \r
5667 /*\r
5668  * Decent random number generator, at least not as bad as Windows\r
5669  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5670  */\r
5671 unsigned int randstate;\r
5672 \r
5673 int\r
5674 myrandom(void)\r
5675 {\r
5676   randstate = randstate * 1664525 + 1013904223;\r
5677   return (int) randstate & 0x7fffffff;\r
5678 }\r
5679 \r
5680 void\r
5681 mysrandom(unsigned int seed)\r
5682 {\r
5683   randstate = seed;\r
5684 }\r
5685 \r
5686 \r
5687 /* \r
5688  * returns TRUE if user selects a different color, FALSE otherwise \r
5689  */\r
5690 \r
5691 BOOL\r
5692 ChangeColor(HWND hwnd, COLORREF *which)\r
5693 {\r
5694   static BOOL firstTime = TRUE;\r
5695   static DWORD customColors[16];\r
5696   CHOOSECOLOR cc;\r
5697   COLORREF newcolor;\r
5698   int i;\r
5699   ColorClass ccl;\r
5700 \r
5701   if (firstTime) {\r
5702     /* Make initial colors in use available as custom colors */\r
5703     /* Should we put the compiled-in defaults here instead? */\r
5704     i = 0;\r
5705     customColors[i++] = lightSquareColor & 0xffffff;\r
5706     customColors[i++] = darkSquareColor & 0xffffff;\r
5707     customColors[i++] = whitePieceColor & 0xffffff;\r
5708     customColors[i++] = blackPieceColor & 0xffffff;\r
5709     customColors[i++] = highlightSquareColor & 0xffffff;\r
5710     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5711 \r
5712     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5713       customColors[i++] = textAttribs[ccl].color;\r
5714     }\r
5715     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5716     firstTime = FALSE;\r
5717   }\r
5718 \r
5719   cc.lStructSize = sizeof(cc);\r
5720   cc.hwndOwner = hwnd;\r
5721   cc.hInstance = NULL;\r
5722   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5723   cc.lpCustColors = (LPDWORD) customColors;\r
5724   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5725 \r
5726   if (!ChooseColor(&cc)) return FALSE;\r
5727 \r
5728   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5729   if (newcolor == *which) return FALSE;\r
5730   *which = newcolor;\r
5731   return TRUE;\r
5732 \r
5733   /*\r
5734   InitDrawingColors();\r
5735   InvalidateRect(hwnd, &boardRect, FALSE);\r
5736   */\r
5737 }\r
5738 \r
5739 BOOLEAN\r
5740 MyLoadSound(MySound *ms)\r
5741 {\r
5742   BOOL ok = FALSE;\r
5743   struct stat st;\r
5744   FILE *f;\r
5745 \r
5746   if (ms->data && ms->flag) free(ms->data);\r
5747   ms->data = NULL;\r
5748 \r
5749   switch (ms->name[0]) {\r
5750   case NULLCHAR:\r
5751     /* Silence */\r
5752     ok = TRUE;\r
5753     break;\r
5754   case '$':\r
5755     /* System sound from Control Panel.  Don't preload here. */\r
5756     ok = TRUE;\r
5757     break;\r
5758   case '!':\r
5759     if (ms->name[1] == NULLCHAR) {\r
5760       /* "!" alone = silence */\r
5761       ok = TRUE;\r
5762     } else {\r
5763       /* Builtin wave resource.  Error if not found. */\r
5764       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5765       if (h == NULL) break;\r
5766       ms->data = (void *)LoadResource(hInst, h);\r
5767       ms->flag = 0; // not maloced, so cannot be freed!\r
5768       if (h == NULL) break;\r
5769       ok = TRUE;\r
5770     }\r
5771     break;\r
5772   default:\r
5773     /* .wav file.  Error if not found. */\r
5774     f = fopen(ms->name, "rb");\r
5775     if (f == NULL) break;\r
5776     if (fstat(fileno(f), &st) < 0) break;\r
5777     ms->data = malloc(st.st_size);\r
5778     ms->flag = 1;\r
5779     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5780     fclose(f);\r
5781     ok = TRUE;\r
5782     break;\r
5783   }\r
5784   if (!ok) {\r
5785     char buf[MSG_SIZ];\r
5786       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5787     DisplayError(buf, GetLastError());\r
5788   }\r
5789   return ok;\r
5790 }\r
5791 \r
5792 BOOLEAN\r
5793 MyPlaySound(MySound *ms)\r
5794 {\r
5795   BOOLEAN ok = FALSE;\r
5796 \r
5797   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5798   switch (ms->name[0]) {\r
5799   case NULLCHAR:\r
5800         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5801     /* Silence */\r
5802     ok = TRUE;\r
5803     break;\r
5804   case '$':\r
5805     /* System sound from Control Panel (deprecated feature).\r
5806        "$" alone or an unset sound name gets default beep (still in use). */\r
5807     if (ms->name[1]) {\r
5808       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5809     }\r
5810     if (!ok) ok = MessageBeep(MB_OK);\r
5811     break; \r
5812   case '!':\r
5813     /* Builtin wave resource, or "!" alone for silence */\r
5814     if (ms->name[1]) {\r
5815       if (ms->data == NULL) return FALSE;\r
5816       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5817     } else {\r
5818       ok = TRUE;\r
5819     }\r
5820     break;\r
5821   default:\r
5822     /* .wav file.  Error if not found. */\r
5823     if (ms->data == NULL) return FALSE;\r
5824     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5825     break;\r
5826   }\r
5827   /* Don't print an error: this can happen innocently if the sound driver\r
5828      is busy; for instance, if another instance of WinBoard is playing\r
5829      a sound at about the same time. */\r
5830   return ok;\r
5831 }\r
5832 \r
5833 \r
5834 LRESULT CALLBACK\r
5835 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5836 {\r
5837   BOOL ok;\r
5838   OPENFILENAME *ofn;\r
5839   static UINT *number; /* gross that this is static */\r
5840 \r
5841   switch (message) {\r
5842   case WM_INITDIALOG: /* message: initialize dialog box */\r
5843     /* Center the dialog over the application window */\r
5844     ofn = (OPENFILENAME *) lParam;\r
5845     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5846       number = (UINT *) ofn->lCustData;\r
5847       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5848     } else {\r
5849       number = NULL;\r
5850     }\r
5851     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5852     Translate(hDlg, 1536);\r
5853     return FALSE;  /* Allow for further processing */\r
5854 \r
5855   case WM_COMMAND:\r
5856     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5857       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5858     }\r
5859     return FALSE;  /* Allow for further processing */\r
5860   }\r
5861   return FALSE;\r
5862 }\r
5863 \r
5864 UINT APIENTRY\r
5865 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5866 {\r
5867   static UINT *number;\r
5868   OPENFILENAME *ofname;\r
5869   OFNOTIFY *ofnot;\r
5870   switch (uiMsg) {\r
5871   case WM_INITDIALOG:\r
5872     Translate(hdlg, DLG_IndexNumber);\r
5873     ofname = (OPENFILENAME *)lParam;\r
5874     number = (UINT *)(ofname->lCustData);\r
5875     break;\r
5876   case WM_NOTIFY:\r
5877     ofnot = (OFNOTIFY *)lParam;\r
5878     if (ofnot->hdr.code == CDN_FILEOK) {\r
5879       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5880     }\r
5881     break;\r
5882   }\r
5883   return 0;\r
5884 }\r
5885 \r
5886 \r
5887 FILE *\r
5888 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5889                char *nameFilt, char *dlgTitle, UINT *number,\r
5890                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5891 {\r
5892   OPENFILENAME openFileName;\r
5893   char buf1[MSG_SIZ];\r
5894   FILE *f;\r
5895 \r
5896   if (fileName == NULL) fileName = buf1;\r
5897   if (defName == NULL) {\r
5898     safeStrCpy(fileName, "*.", 3 );\r
5899     strcat(fileName, defExt);\r
5900   } else {\r
5901     safeStrCpy(fileName, defName, MSG_SIZ );\r
5902   }\r
5903     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5904   if (number) *number = 0;\r
5905 \r
5906   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5907   openFileName.hwndOwner         = hwnd;\r
5908   openFileName.hInstance         = (HANDLE) hInst;\r
5909   openFileName.lpstrFilter       = nameFilt;\r
5910   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5911   openFileName.nMaxCustFilter    = 0L;\r
5912   openFileName.nFilterIndex      = 1L;\r
5913   openFileName.lpstrFile         = fileName;\r
5914   openFileName.nMaxFile          = MSG_SIZ;\r
5915   openFileName.lpstrFileTitle    = fileTitle;\r
5916   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5917   openFileName.lpstrInitialDir   = NULL;\r
5918   openFileName.lpstrTitle        = dlgTitle;\r
5919   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5920     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5921     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5922     | (oldDialog ? 0 : OFN_EXPLORER);\r
5923   openFileName.nFileOffset       = 0;\r
5924   openFileName.nFileExtension    = 0;\r
5925   openFileName.lpstrDefExt       = defExt;\r
5926   openFileName.lCustData         = (LONG) number;\r
5927   openFileName.lpfnHook          = oldDialog ?\r
5928     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5929   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5930 \r
5931   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5932                         GetOpenFileName(&openFileName)) {\r
5933     /* open the file */\r
5934     f = fopen(openFileName.lpstrFile, write);\r
5935     if (f == NULL) {\r
5936       MessageBox(hwnd, _("File open failed"), NULL,\r
5937                  MB_OK|MB_ICONEXCLAMATION);\r
5938       return NULL;\r
5939     }\r
5940   } else {\r
5941     int err = CommDlgExtendedError();\r
5942     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5943     return FALSE;\r
5944   }\r
5945   return f;\r
5946 }\r
5947 \r
5948 \r
5949 \r
5950 VOID APIENTRY\r
5951 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5952 {\r
5953   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5954 \r
5955   /*\r
5956    * Get the first pop-up menu in the menu template. This is the\r
5957    * menu that TrackPopupMenu displays.\r
5958    */\r
5959   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5960   TranslateOneMenu(10, hmenuTrackPopup);\r
5961 \r
5962   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5963 \r
5964   /*\r
5965    * TrackPopup uses screen coordinates, so convert the\r
5966    * coordinates of the mouse click to screen coordinates.\r
5967    */\r
5968   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5969 \r
5970   /* Draw and track the floating pop-up menu. */\r
5971   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5972                  pt.x, pt.y, 0, hwnd, NULL);\r
5973 \r
5974   /* Destroy the menu.*/\r
5975   DestroyMenu(hmenu);\r
5976 }\r
5977    \r
5978 typedef struct {\r
5979   HWND hDlg, hText;\r
5980   int sizeX, sizeY, newSizeX, newSizeY;\r
5981   HDWP hdwp;\r
5982 } ResizeEditPlusButtonsClosure;\r
5983 \r
5984 BOOL CALLBACK\r
5985 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5986 {\r
5987   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5988   RECT rect;\r
5989   POINT pt;\r
5990 \r
5991   if (hChild == cl->hText) return TRUE;\r
5992   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5993   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5994   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5995   ScreenToClient(cl->hDlg, &pt);\r
5996   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5997     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5998   return TRUE;\r
5999 }\r
6000 \r
6001 /* Resize a dialog that has a (rich) edit field filling most of\r
6002    the top, with a row of buttons below */\r
6003 VOID\r
6004 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6005 {\r
6006   RECT rectText;\r
6007   int newTextHeight, newTextWidth;\r
6008   ResizeEditPlusButtonsClosure cl;\r
6009   \r
6010   /*if (IsIconic(hDlg)) return;*/\r
6011   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6012   \r
6013   cl.hdwp = BeginDeferWindowPos(8);\r
6014 \r
6015   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6016   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6017   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6018   if (newTextHeight < 0) {\r
6019     newSizeY += -newTextHeight;\r
6020     newTextHeight = 0;\r
6021   }\r
6022   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6023     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6024 \r
6025   cl.hDlg = hDlg;\r
6026   cl.hText = hText;\r
6027   cl.sizeX = sizeX;\r
6028   cl.sizeY = sizeY;\r
6029   cl.newSizeX = newSizeX;\r
6030   cl.newSizeY = newSizeY;\r
6031   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6032 \r
6033   EndDeferWindowPos(cl.hdwp);\r
6034 }\r
6035 \r
6036 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6037 {\r
6038     RECT    rChild, rParent;\r
6039     int     wChild, hChild, wParent, hParent;\r
6040     int     wScreen, hScreen, xNew, yNew;\r
6041     HDC     hdc;\r
6042 \r
6043     /* Get the Height and Width of the child window */\r
6044     GetWindowRect (hwndChild, &rChild);\r
6045     wChild = rChild.right - rChild.left;\r
6046     hChild = rChild.bottom - rChild.top;\r
6047 \r
6048     /* Get the Height and Width of the parent window */\r
6049     GetWindowRect (hwndParent, &rParent);\r
6050     wParent = rParent.right - rParent.left;\r
6051     hParent = rParent.bottom - rParent.top;\r
6052 \r
6053     /* Get the display limits */\r
6054     hdc = GetDC (hwndChild);\r
6055     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6056     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6057     ReleaseDC(hwndChild, hdc);\r
6058 \r
6059     /* Calculate new X position, then adjust for screen */\r
6060     xNew = rParent.left + ((wParent - wChild) /2);\r
6061     if (xNew < 0) {\r
6062         xNew = 0;\r
6063     } else if ((xNew+wChild) > wScreen) {\r
6064         xNew = wScreen - wChild;\r
6065     }\r
6066 \r
6067     /* Calculate new Y position, then adjust for screen */\r
6068     if( mode == 0 ) {\r
6069         yNew = rParent.top  + ((hParent - hChild) /2);\r
6070     }\r
6071     else {\r
6072         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6073     }\r
6074 \r
6075     if (yNew < 0) {\r
6076         yNew = 0;\r
6077     } else if ((yNew+hChild) > hScreen) {\r
6078         yNew = hScreen - hChild;\r
6079     }\r
6080 \r
6081     /* Set it, and return */\r
6082     return SetWindowPos (hwndChild, NULL,\r
6083                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6084 }\r
6085 \r
6086 /* Center one window over another */\r
6087 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6088 {\r
6089     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6090 }\r
6091 \r
6092 /*---------------------------------------------------------------------------*\\r
6093  *\r
6094  * Startup Dialog functions\r
6095  *\r
6096 \*---------------------------------------------------------------------------*/\r
6097 void\r
6098 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6099 {\r
6100   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6101 \r
6102   while (*cd != NULL) {\r
6103     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6104     cd++;\r
6105   }\r
6106 }\r
6107 \r
6108 void\r
6109 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6110 {\r
6111   char buf1[MAX_ARG_LEN];\r
6112   int len;\r
6113 \r
6114   if (str[0] == '@') {\r
6115     FILE* f = fopen(str + 1, "r");\r
6116     if (f == NULL) {\r
6117       DisplayFatalError(str + 1, errno, 2);\r
6118       return;\r
6119     }\r
6120     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6121     fclose(f);\r
6122     buf1[len] = NULLCHAR;\r
6123     str = buf1;\r
6124   }\r
6125 \r
6126   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6127 \r
6128   for (;;) {\r
6129     char buf[MSG_SIZ];\r
6130     char *end = strchr(str, '\n');\r
6131     if (end == NULL) return;\r
6132     memcpy(buf, str, end - str);\r
6133     buf[end - str] = NULLCHAR;\r
6134     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6135     str = end + 1;\r
6136   }\r
6137 }\r
6138 \r
6139 void\r
6140 SetStartupDialogEnables(HWND hDlg)\r
6141 {\r
6142   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6143     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6144     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6145   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6146     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6147   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6148     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6149   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6150     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6151   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6152     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6153     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6154     IsDlgButtonChecked(hDlg, OPT_View));\r
6155 }\r
6156 \r
6157 char *\r
6158 QuoteForFilename(char *filename)\r
6159 {\r
6160   int dquote, space;\r
6161   dquote = strchr(filename, '"') != NULL;\r
6162   space = strchr(filename, ' ') != NULL;\r
6163   if (dquote || space) {\r
6164     if (dquote) {\r
6165       return "'";\r
6166     } else {\r
6167       return "\"";\r
6168     }\r
6169   } else {\r
6170     return "";\r
6171   }\r
6172 }\r
6173 \r
6174 VOID\r
6175 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6176 {\r
6177   char buf[MSG_SIZ];\r
6178   char *q;\r
6179 \r
6180   InitComboStringsFromOption(hwndCombo, nthnames);\r
6181   q = QuoteForFilename(nthcp);\r
6182     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6183   if (*nthdir != NULLCHAR) {\r
6184     q = QuoteForFilename(nthdir);\r
6185       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6186   }\r
6187   if (*nthcp == NULLCHAR) {\r
6188     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6189   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6190     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6191     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6192   }\r
6193 }\r
6194 \r
6195 LRESULT CALLBACK\r
6196 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6197 {\r
6198   char buf[MSG_SIZ];\r
6199   HANDLE hwndCombo;\r
6200   char *p;\r
6201 \r
6202   switch (message) {\r
6203   case WM_INITDIALOG:\r
6204     /* Center the dialog */\r
6205     CenterWindow (hDlg, GetDesktopWindow());\r
6206     Translate(hDlg, DLG_Startup);\r
6207     /* Initialize the dialog items */\r
6208     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6209                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6210                   firstChessProgramNames);\r
6211     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6212                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6213                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6214     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6215     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6216       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6217     if (*appData.icsHelper != NULLCHAR) {\r
6218       char *q = QuoteForFilename(appData.icsHelper);\r
6219       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6220     }\r
6221     if (*appData.icsHost == NULLCHAR) {\r
6222       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6223       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6224     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6225       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6226       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6227     }\r
6228 \r
6229     if (appData.icsActive) {\r
6230       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6231     }\r
6232     else if (appData.noChessProgram) {\r
6233       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6234     }\r
6235     else {\r
6236       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6237     }\r
6238 \r
6239     SetStartupDialogEnables(hDlg);\r
6240     return TRUE;\r
6241 \r
6242   case WM_COMMAND:\r
6243     switch (LOWORD(wParam)) {\r
6244     case IDOK:\r
6245       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6246         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6247         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6248         p = buf;\r
6249         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6250         ParseArgs(StringGet, &p);\r
6251         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6252         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6253         p = buf;\r
6254         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6255         ParseArgs(StringGet, &p);\r
6256         SwapEngines(singleList); // ... and then make it 'second'\r
6257         appData.noChessProgram = FALSE;\r
6258         appData.icsActive = FALSE;\r
6259       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6260         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6261         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6262         p = buf;\r
6263         ParseArgs(StringGet, &p);\r
6264         if (appData.zippyPlay) {\r
6265           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6266           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6267           p = buf;\r
6268           ParseArgs(StringGet, &p);\r
6269         }\r
6270       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6271         appData.noChessProgram = TRUE;\r
6272         appData.icsActive = FALSE;\r
6273       } else {\r
6274         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6275                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6276         return TRUE;\r
6277       }\r
6278       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6279         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6280         p = buf;\r
6281         ParseArgs(StringGet, &p);\r
6282       }\r
6283       EndDialog(hDlg, TRUE);\r
6284       return TRUE;\r
6285 \r
6286     case IDCANCEL:\r
6287       ExitEvent(0);\r
6288       return TRUE;\r
6289 \r
6290     case IDM_HELPCONTENTS:\r
6291       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6292         MessageBox (GetFocus(),\r
6293                     _("Unable to activate help"),\r
6294                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6295       }\r
6296       break;\r
6297 \r
6298     default:\r
6299       SetStartupDialogEnables(hDlg);\r
6300       break;\r
6301     }\r
6302     break;\r
6303   }\r
6304   return FALSE;\r
6305 }\r
6306 \r
6307 /*---------------------------------------------------------------------------*\\r
6308  *\r
6309  * About box dialog functions\r
6310  *\r
6311 \*---------------------------------------------------------------------------*/\r
6312 \r
6313 /* Process messages for "About" dialog box */\r
6314 LRESULT CALLBACK\r
6315 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6316 {\r
6317   switch (message) {\r
6318   case WM_INITDIALOG: /* message: initialize dialog box */\r
6319     /* Center the dialog over the application window */\r
6320     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6321     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6322     Translate(hDlg, ABOUTBOX);\r
6323     JAWS_COPYRIGHT\r
6324     return (TRUE);\r
6325 \r
6326   case WM_COMMAND: /* message: received a command */\r
6327     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6328         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6329       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6330       return (TRUE);\r
6331     }\r
6332     break;\r
6333   }\r
6334   return (FALSE);\r
6335 }\r
6336 \r
6337 /*---------------------------------------------------------------------------*\\r
6338  *\r
6339  * Comment Dialog functions\r
6340  *\r
6341 \*---------------------------------------------------------------------------*/\r
6342 \r
6343 LRESULT CALLBACK\r
6344 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6345 {\r
6346   static HANDLE hwndText = NULL;\r
6347   int len, newSizeX, newSizeY, flags;\r
6348   static int sizeX, sizeY;\r
6349   char *str;\r
6350   RECT rect;\r
6351   MINMAXINFO *mmi;\r
6352 \r
6353   switch (message) {\r
6354   case WM_INITDIALOG: /* message: initialize dialog box */\r
6355     /* Initialize the dialog items */\r
6356     Translate(hDlg, DLG_EditComment);\r
6357     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6358     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6359     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6360     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6361     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6362     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6363     SetWindowText(hDlg, commentTitle);\r
6364     if (editComment) {\r
6365       SetFocus(hwndText);\r
6366     } else {\r
6367       SetFocus(GetDlgItem(hDlg, IDOK));\r
6368     }\r
6369     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6370                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6371                 MAKELPARAM(FALSE, 0));\r
6372     /* Size and position the dialog */\r
6373     if (!commentDialog) {\r
6374       commentDialog = hDlg;\r
6375       flags = SWP_NOZORDER;\r
6376       GetClientRect(hDlg, &rect);\r
6377       sizeX = rect.right;\r
6378       sizeY = rect.bottom;\r
6379       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6380           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6381         WINDOWPLACEMENT wp;\r
6382         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6383         wp.length = sizeof(WINDOWPLACEMENT);\r
6384         wp.flags = 0;\r
6385         wp.showCmd = SW_SHOW;\r
6386         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6387         wp.rcNormalPosition.left = wpComment.x;\r
6388         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6389         wp.rcNormalPosition.top = wpComment.y;\r
6390         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6391         SetWindowPlacement(hDlg, &wp);\r
6392 \r
6393         GetClientRect(hDlg, &rect);\r
6394         newSizeX = rect.right;\r
6395         newSizeY = rect.bottom;\r
6396         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6397                               newSizeX, newSizeY);\r
6398         sizeX = newSizeX;\r
6399         sizeY = newSizeY;\r
6400       }\r
6401     }\r
6402     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6403     return FALSE;\r
6404 \r
6405   case WM_COMMAND: /* message: received a command */\r
6406     switch (LOWORD(wParam)) {\r
6407     case IDOK:\r
6408       if (editComment) {\r
6409         char *p, *q;\r
6410         /* Read changed options from the dialog box */\r
6411         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6412         len = GetWindowTextLength(hwndText);\r
6413         str = (char *) malloc(len + 1);\r
6414         GetWindowText(hwndText, str, len + 1);\r
6415         p = q = str;\r
6416         while (*q) {\r
6417           if (*q == '\r')\r
6418             q++;\r
6419           else\r
6420             *p++ = *q++;\r
6421         }\r
6422         *p = NULLCHAR;\r
6423         ReplaceComment(commentIndex, str);\r
6424         free(str);\r
6425       }\r
6426       CommentPopDown();\r
6427       return TRUE;\r
6428 \r
6429     case IDCANCEL:\r
6430     case OPT_CancelComment:\r
6431       CommentPopDown();\r
6432       return TRUE;\r
6433 \r
6434     case OPT_ClearComment:\r
6435       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6436       break;\r
6437 \r
6438     case OPT_EditComment:\r
6439       EditCommentEvent();\r
6440       return TRUE;\r
6441 \r
6442     default:\r
6443       break;\r
6444     }\r
6445     break;\r
6446 \r
6447   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6448         if( wParam == OPT_CommentText ) {\r
6449             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6450 \r
6451             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6452                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6453                 POINTL pt;\r
6454                 LRESULT index;\r
6455 \r
6456                 pt.x = LOWORD( lpMF->lParam );\r
6457                 pt.y = HIWORD( lpMF->lParam );\r
6458 \r
6459                 if(lpMF->msg == WM_CHAR) {\r
6460                         CHARRANGE sel;\r
6461                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6462                         index = sel.cpMin;\r
6463                 } else\r
6464                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6465 \r
6466                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6467                 len = GetWindowTextLength(hwndText);\r
6468                 str = (char *) malloc(len + 1);\r
6469                 GetWindowText(hwndText, str, len + 1);\r
6470                 ReplaceComment(commentIndex, str);\r
6471                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6472                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6473                 free(str);\r
6474 \r
6475                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6476                 lpMF->msg = WM_USER;\r
6477 \r
6478                 return TRUE;\r
6479             }\r
6480         }\r
6481         break;\r
6482 \r
6483   case WM_SIZE:\r
6484     newSizeX = LOWORD(lParam);\r
6485     newSizeY = HIWORD(lParam);\r
6486     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6487     sizeX = newSizeX;\r
6488     sizeY = newSizeY;\r
6489     break;\r
6490 \r
6491   case WM_GETMINMAXINFO:\r
6492     /* Prevent resizing window too small */\r
6493     mmi = (MINMAXINFO *) lParam;\r
6494     mmi->ptMinTrackSize.x = 100;\r
6495     mmi->ptMinTrackSize.y = 100;\r
6496     break;\r
6497   }\r
6498   return FALSE;\r
6499 }\r
6500 \r
6501 VOID\r
6502 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6503 {\r
6504   FARPROC lpProc;\r
6505   char *p, *q;\r
6506 \r
6507   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6508 \r
6509   if (str == NULL) str = "";\r
6510   p = (char *) malloc(2 * strlen(str) + 2);\r
6511   q = p;\r
6512   while (*str) {\r
6513     if (*str == '\n') *q++ = '\r';\r
6514     *q++ = *str++;\r
6515   }\r
6516   *q = NULLCHAR;\r
6517   if (commentText != NULL) free(commentText);\r
6518 \r
6519   commentIndex = index;\r
6520   commentTitle = title;\r
6521   commentText = p;\r
6522   editComment = edit;\r
6523 \r
6524   if (commentDialog) {\r
6525     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6526     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6527   } else {\r
6528     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6529     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6530                  hwndMain, (DLGPROC)lpProc);\r
6531     FreeProcInstance(lpProc);\r
6532   }\r
6533   commentUp = TRUE;\r
6534 }\r
6535 \r
6536 \r
6537 /*---------------------------------------------------------------------------*\\r
6538  *\r
6539  * Type-in move dialog functions\r
6540  * \r
6541 \*---------------------------------------------------------------------------*/\r
6542 \r
6543 LRESULT CALLBACK\r
6544 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6545 {\r
6546   char move[MSG_SIZ];\r
6547   HWND hInput;\r
6548 \r
6549   switch (message) {\r
6550   case WM_INITDIALOG:\r
6551     move[0] = (char) lParam;\r
6552     move[1] = NULLCHAR;\r
6553     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6554     Translate(hDlg, DLG_TypeInMove);\r
6555     hInput = GetDlgItem(hDlg, OPT_Move);\r
6556     SetWindowText(hInput, move);\r
6557     SetFocus(hInput);\r
6558     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6559     return FALSE;\r
6560 \r
6561   case WM_COMMAND:\r
6562     switch (LOWORD(wParam)) {\r
6563     case IDOK:\r
6564 \r
6565       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6566       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6567       TypeInDoneEvent(move);\r
6568       EndDialog(hDlg, TRUE);\r
6569       return TRUE;\r
6570     case IDCANCEL:\r
6571       EndDialog(hDlg, FALSE);\r
6572       return TRUE;\r
6573     default:\r
6574       break;\r
6575     }\r
6576     break;\r
6577   }\r
6578   return FALSE;\r
6579 }\r
6580 \r
6581 VOID\r
6582 PopUpMoveDialog(char firstchar)\r
6583 {\r
6584     FARPROC lpProc;\r
6585 \r
6586       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6587       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6588         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6589       FreeProcInstance(lpProc);\r
6590 }\r
6591 \r
6592 /*---------------------------------------------------------------------------*\\r
6593  *\r
6594  * Type-in name dialog functions\r
6595  * \r
6596 \*---------------------------------------------------------------------------*/\r
6597 \r
6598 LRESULT CALLBACK\r
6599 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6600 {\r
6601   char move[MSG_SIZ];\r
6602   HWND hInput;\r
6603 \r
6604   switch (message) {\r
6605   case WM_INITDIALOG:\r
6606     move[0] = (char) lParam;\r
6607     move[1] = NULLCHAR;\r
6608     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6609     Translate(hDlg, DLG_TypeInName);\r
6610     hInput = GetDlgItem(hDlg, OPT_Name);\r
6611     SetWindowText(hInput, move);\r
6612     SetFocus(hInput);\r
6613     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6614     return FALSE;\r
6615 \r
6616   case WM_COMMAND:\r
6617     switch (LOWORD(wParam)) {\r
6618     case IDOK:\r
6619       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6620       appData.userName = strdup(move);\r
6621       SetUserLogo();\r
6622       SetGameInfo();\r
6623       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6624         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6625         DisplayTitle(move);\r
6626       }\r
6627 \r
6628 \r
6629       EndDialog(hDlg, TRUE);\r
6630       return TRUE;\r
6631     case IDCANCEL:\r
6632       EndDialog(hDlg, FALSE);\r
6633       return TRUE;\r
6634     default:\r
6635       break;\r
6636     }\r
6637     break;\r
6638   }\r
6639   return FALSE;\r
6640 }\r
6641 \r
6642 VOID\r
6643 PopUpNameDialog(char firstchar)\r
6644 {\r
6645     FARPROC lpProc;\r
6646     \r
6647       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6648       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6649         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6650       FreeProcInstance(lpProc);\r
6651 }\r
6652 \r
6653 /*---------------------------------------------------------------------------*\\r
6654  *\r
6655  *  Error dialogs\r
6656  * \r
6657 \*---------------------------------------------------------------------------*/\r
6658 \r
6659 /* Nonmodal error box */\r
6660 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6661                              WPARAM wParam, LPARAM lParam);\r
6662 \r
6663 VOID\r
6664 ErrorPopUp(char *title, char *content)\r
6665 {\r
6666   FARPROC lpProc;\r
6667   char *p, *q;\r
6668   BOOLEAN modal = hwndMain == NULL;\r
6669 \r
6670   p = content;\r
6671   q = errorMessage;\r
6672   while (*p) {\r
6673     if (*p == '\n') {\r
6674       if (modal) {\r
6675         *q++ = ' ';\r
6676         p++;\r
6677       } else {\r
6678         *q++ = '\r';\r
6679         *q++ = *p++;\r
6680       }\r
6681     } else {\r
6682       *q++ = *p++;\r
6683     }\r
6684   }\r
6685   *q = NULLCHAR;\r
6686   strncpy(errorTitle, title, sizeof(errorTitle));\r
6687   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6688   \r
6689   if (modal) {\r
6690     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6691   } else {\r
6692     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6693     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6694                  hwndMain, (DLGPROC)lpProc);\r
6695     FreeProcInstance(lpProc);\r
6696   }\r
6697 }\r
6698 \r
6699 VOID\r
6700 ErrorPopDown()\r
6701 {\r
6702   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6703   if (errorDialog == NULL) return;\r
6704   DestroyWindow(errorDialog);\r
6705   errorDialog = NULL;\r
6706   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6707 }\r
6708 \r
6709 LRESULT CALLBACK\r
6710 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6711 {\r
6712   HANDLE hwndText;\r
6713   RECT rChild;\r
6714 \r
6715   switch (message) {\r
6716   case WM_INITDIALOG:\r
6717     GetWindowRect(hDlg, &rChild);\r
6718 \r
6719     /*\r
6720     SetWindowPos(hDlg, NULL, rChild.left,\r
6721       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6722       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6723     */\r
6724 \r
6725     /* \r
6726         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6727         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6728         and it doesn't work when you resize the dialog.\r
6729         For now, just give it a default position.\r
6730     */\r
6731     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6732     Translate(hDlg, DLG_Error);\r
6733 \r
6734     errorDialog = hDlg;\r
6735     SetWindowText(hDlg, errorTitle);\r
6736     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6737     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6738     return FALSE;\r
6739 \r
6740   case WM_COMMAND:\r
6741     switch (LOWORD(wParam)) {\r
6742     case IDOK:\r
6743     case IDCANCEL:\r
6744       if (errorDialog == hDlg) errorDialog = NULL;\r
6745       DestroyWindow(hDlg);\r
6746       return TRUE;\r
6747 \r
6748     default:\r
6749       break;\r
6750     }\r
6751     break;\r
6752   }\r
6753   return FALSE;\r
6754 }\r
6755 \r
6756 #ifdef GOTHIC\r
6757 HWND gothicDialog = NULL;\r
6758 \r
6759 LRESULT CALLBACK\r
6760 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6761 {\r
6762   HANDLE hwndText;\r
6763   RECT rChild;\r
6764   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6765 \r
6766   switch (message) {\r
6767   case WM_INITDIALOG:\r
6768     GetWindowRect(hDlg, &rChild);\r
6769 \r
6770     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6771                                                              SWP_NOZORDER);\r
6772 \r
6773     /* \r
6774         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6775         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6776         and it doesn't work when you resize the dialog.\r
6777         For now, just give it a default position.\r
6778     */\r
6779     gothicDialog = hDlg;\r
6780     SetWindowText(hDlg, errorTitle);\r
6781     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6782     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6783     return FALSE;\r
6784 \r
6785   case WM_COMMAND:\r
6786     switch (LOWORD(wParam)) {\r
6787     case IDOK:\r
6788     case IDCANCEL:\r
6789       if (errorDialog == hDlg) errorDialog = NULL;\r
6790       DestroyWindow(hDlg);\r
6791       return TRUE;\r
6792 \r
6793     default:\r
6794       break;\r
6795     }\r
6796     break;\r
6797   }\r
6798   return FALSE;\r
6799 }\r
6800 \r
6801 VOID\r
6802 GothicPopUp(char *title, VariantClass variant)\r
6803 {\r
6804   FARPROC lpProc;\r
6805   static char *lastTitle;\r
6806 \r
6807   strncpy(errorTitle, title, sizeof(errorTitle));\r
6808   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6809 \r
6810   if(lastTitle != title && gothicDialog != NULL) {\r
6811     DestroyWindow(gothicDialog);\r
6812     gothicDialog = NULL;\r
6813   }\r
6814   if(variant != VariantNormal && gothicDialog == NULL) {\r
6815     title = lastTitle;\r
6816     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6817     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6818                  hwndMain, (DLGPROC)lpProc);\r
6819     FreeProcInstance(lpProc);\r
6820   }\r
6821 }\r
6822 #endif\r
6823 \r
6824 /*---------------------------------------------------------------------------*\\r
6825  *\r
6826  *  Ics Interaction console functions\r
6827  *\r
6828 \*---------------------------------------------------------------------------*/\r
6829 \r
6830 #define HISTORY_SIZE 64\r
6831 static char *history[HISTORY_SIZE];\r
6832 int histIn = 0, histP = 0;\r
6833 \r
6834 VOID\r
6835 SaveInHistory(char *cmd)\r
6836 {\r
6837   if (history[histIn] != NULL) {\r
6838     free(history[histIn]);\r
6839     history[histIn] = NULL;\r
6840   }\r
6841   if (*cmd == NULLCHAR) return;\r
6842   history[histIn] = StrSave(cmd);\r
6843   histIn = (histIn + 1) % HISTORY_SIZE;\r
6844   if (history[histIn] != NULL) {\r
6845     free(history[histIn]);\r
6846     history[histIn] = NULL;\r
6847   }\r
6848   histP = histIn;\r
6849 }\r
6850 \r
6851 char *\r
6852 PrevInHistory(char *cmd)\r
6853 {\r
6854   int newhp;\r
6855   if (histP == histIn) {\r
6856     if (history[histIn] != NULL) free(history[histIn]);\r
6857     history[histIn] = StrSave(cmd);\r
6858   }\r
6859   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6860   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6861   histP = newhp;\r
6862   return history[histP];\r
6863 }\r
6864 \r
6865 char *\r
6866 NextInHistory()\r
6867 {\r
6868   if (histP == histIn) return NULL;\r
6869   histP = (histP + 1) % HISTORY_SIZE;\r
6870   return history[histP];   \r
6871 }\r
6872 \r
6873 HMENU\r
6874 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6875 {\r
6876   HMENU hmenu, h;\r
6877   int i = 0;\r
6878   hmenu = LoadMenu(hInst, "TextMenu");\r
6879   h = GetSubMenu(hmenu, 0);\r
6880   while (e->item) {\r
6881     if (strcmp(e->item, "-") == 0) {\r
6882       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6883     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6884       int flags = MF_STRING, j = 0;\r
6885       if (e->item[0] == '|') {\r
6886         flags |= MF_MENUBARBREAK;\r
6887         j++;\r
6888       }\r
6889       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6890       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6891     }\r
6892     e++;\r
6893     i++;\r
6894   } \r
6895   return hmenu;\r
6896 }\r
6897 \r
6898 WNDPROC consoleTextWindowProc;\r
6899 \r
6900 void\r
6901 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6902 {\r
6903   char buf[MSG_SIZ], name[MSG_SIZ];\r
6904   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6905   CHARRANGE sel;\r
6906 \r
6907   if (!getname) {\r
6908     SetWindowText(hInput, command);\r
6909     if (immediate) {\r
6910       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6911     } else {\r
6912       sel.cpMin = 999999;\r
6913       sel.cpMax = 999999;\r
6914       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6915       SetFocus(hInput);\r
6916     }\r
6917     return;\r
6918   }    \r
6919   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6920   if (sel.cpMin == sel.cpMax) {\r
6921     /* Expand to surrounding word */\r
6922     TEXTRANGE tr;\r
6923     do {\r
6924       tr.chrg.cpMax = sel.cpMin;\r
6925       tr.chrg.cpMin = --sel.cpMin;\r
6926       if (sel.cpMin < 0) break;\r
6927       tr.lpstrText = name;\r
6928       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6929     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6930     sel.cpMin++;\r
6931 \r
6932     do {\r
6933       tr.chrg.cpMin = sel.cpMax;\r
6934       tr.chrg.cpMax = ++sel.cpMax;\r
6935       tr.lpstrText = name;\r
6936       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6937     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6938     sel.cpMax--;\r
6939 \r
6940     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6941       MessageBeep(MB_ICONEXCLAMATION);\r
6942       return;\r
6943     }\r
6944     tr.chrg = sel;\r
6945     tr.lpstrText = name;\r
6946     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6947   } else {\r
6948     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6949       MessageBeep(MB_ICONEXCLAMATION);\r
6950       return;\r
6951     }\r
6952     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6953   }\r
6954   if (immediate) {\r
6955     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6956     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6957     SetWindowText(hInput, buf);\r
6958     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6959   } else {\r
6960     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6961       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6962     SetWindowText(hInput, buf);\r
6963     sel.cpMin = 999999;\r
6964     sel.cpMax = 999999;\r
6965     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6966     SetFocus(hInput);\r
6967   }\r
6968 }\r
6969 \r
6970 LRESULT CALLBACK \r
6971 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6972 {\r
6973   HWND hInput;\r
6974   CHARRANGE sel;\r
6975 \r
6976   switch (message) {\r
6977   case WM_KEYDOWN:\r
6978     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6979     if(wParam=='R') return 0;\r
6980     switch (wParam) {\r
6981     case VK_PRIOR:\r
6982       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6983       return 0;\r
6984     case VK_NEXT:\r
6985       sel.cpMin = 999999;\r
6986       sel.cpMax = 999999;\r
6987       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6988       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6989       return 0;\r
6990     }\r
6991     break;\r
6992   case WM_CHAR:\r
6993    if(wParam != '\022') {\r
6994     if (wParam == '\t') {\r
6995       if (GetKeyState(VK_SHIFT) < 0) {\r
6996         /* shifted */\r
6997         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6998         if (buttonDesc[0].hwnd) {\r
6999           SetFocus(buttonDesc[0].hwnd);\r
7000         } else {\r
7001           SetFocus(hwndMain);\r
7002         }\r
7003       } else {\r
7004         /* unshifted */\r
7005         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7006       }\r
7007     } else {\r
7008       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7009       JAWS_DELETE( SetFocus(hInput); )\r
7010       SendMessage(hInput, message, wParam, lParam);\r
7011     }\r
7012     return 0;\r
7013    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7014    lParam = -1;\r
7015   case WM_RBUTTONDOWN:\r
7016     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7017       /* Move selection here if it was empty */\r
7018       POINT pt;\r
7019       pt.x = LOWORD(lParam);\r
7020       pt.y = HIWORD(lParam);\r
7021       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7022       if (sel.cpMin == sel.cpMax) {\r
7023         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7024         sel.cpMax = sel.cpMin;\r
7025         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7026       }\r
7027       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7028 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7029       POINT pt;\r
7030       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7031       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7032       if (sel.cpMin == sel.cpMax) {\r
7033         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7034         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7035       }\r
7036       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7037         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7038       }\r
7039       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7040       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7041       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7042       MenuPopup(hwnd, pt, hmenu, -1);\r
7043 }\r
7044     }\r
7045     return 0;\r
7046   case WM_RBUTTONUP:\r
7047     if (GetKeyState(VK_SHIFT) & ~1) {\r
7048       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7049         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7050     }\r
7051     return 0;\r
7052   case WM_PASTE:\r
7053     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7054     SetFocus(hInput);\r
7055     return SendMessage(hInput, message, wParam, lParam);\r
7056   case WM_MBUTTONDOWN:\r
7057     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7058   case WM_COMMAND:\r
7059     switch (LOWORD(wParam)) {\r
7060     case IDM_QuickPaste:\r
7061       {\r
7062         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7063         if (sel.cpMin == sel.cpMax) {\r
7064           MessageBeep(MB_ICONEXCLAMATION);\r
7065           return 0;\r
7066         }\r
7067         SendMessage(hwnd, WM_COPY, 0, 0);\r
7068         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7069         SendMessage(hInput, WM_PASTE, 0, 0);\r
7070         SetFocus(hInput);\r
7071         return 0;\r
7072       }\r
7073     case IDM_Cut:\r
7074       SendMessage(hwnd, WM_CUT, 0, 0);\r
7075       return 0;\r
7076     case IDM_Paste:\r
7077       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7078       return 0;\r
7079     case IDM_Copy:\r
7080       SendMessage(hwnd, WM_COPY, 0, 0);\r
7081       return 0;\r
7082     default:\r
7083       {\r
7084         int i = LOWORD(wParam) - IDM_CommandX;\r
7085         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7086             icsTextMenuEntry[i].command != NULL) {\r
7087           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7088                    icsTextMenuEntry[i].getname,\r
7089                    icsTextMenuEntry[i].immediate);\r
7090           return 0;\r
7091         }\r
7092       }\r
7093       break;\r
7094     }\r
7095     break;\r
7096   }\r
7097   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7098 }\r
7099 \r
7100 WNDPROC consoleInputWindowProc;\r
7101 \r
7102 LRESULT CALLBACK\r
7103 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7104 {\r
7105   char buf[MSG_SIZ];\r
7106   char *p;\r
7107   static BOOL sendNextChar = FALSE;\r
7108   static BOOL quoteNextChar = FALSE;\r
7109   InputSource *is = consoleInputSource;\r
7110   CHARFORMAT cf;\r
7111   CHARRANGE sel;\r
7112 \r
7113   switch (message) {\r
7114   case WM_CHAR:\r
7115     if (!appData.localLineEditing || sendNextChar) {\r
7116       is->buf[0] = (CHAR) wParam;\r
7117       is->count = 1;\r
7118       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7119       sendNextChar = FALSE;\r
7120       return 0;\r
7121     }\r
7122     if (quoteNextChar) {\r
7123       buf[0] = (char) wParam;\r
7124       buf[1] = NULLCHAR;\r
7125       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7126       quoteNextChar = FALSE;\r
7127       return 0;\r
7128     }\r
7129     switch (wParam) {\r
7130     case '\r':   /* Enter key */\r
7131       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7132       if (consoleEcho) SaveInHistory(is->buf);\r
7133       is->buf[is->count++] = '\n';\r
7134       is->buf[is->count] = NULLCHAR;\r
7135       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7136       if (consoleEcho) {\r
7137         ConsoleOutput(is->buf, is->count, TRUE);\r
7138       } else if (appData.localLineEditing) {\r
7139         ConsoleOutput("\n", 1, TRUE);\r
7140       }\r
7141       /* fall thru */\r
7142     case '\033': /* Escape key */\r
7143       SetWindowText(hwnd, "");\r
7144       cf.cbSize = sizeof(CHARFORMAT);\r
7145       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7146       if (consoleEcho) {\r
7147         cf.crTextColor = textAttribs[ColorNormal].color;\r
7148       } else {\r
7149         cf.crTextColor = COLOR_ECHOOFF;\r
7150       }\r
7151       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7152       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7153       return 0;\r
7154     case '\t':   /* Tab key */\r
7155       if (GetKeyState(VK_SHIFT) < 0) {\r
7156         /* shifted */\r
7157         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7158       } else {\r
7159         /* unshifted */\r
7160         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7161         if (buttonDesc[0].hwnd) {\r
7162           SetFocus(buttonDesc[0].hwnd);\r
7163         } else {\r
7164           SetFocus(hwndMain);\r
7165         }\r
7166       }\r
7167       return 0;\r
7168     case '\023': /* Ctrl+S */\r
7169       sendNextChar = TRUE;\r
7170       return 0;\r
7171     case '\021': /* Ctrl+Q */\r
7172       quoteNextChar = TRUE;\r
7173       return 0;\r
7174     JAWS_REPLAY\r
7175     default:\r
7176       break;\r
7177     }\r
7178     break;\r
7179   case WM_KEYDOWN:\r
7180     switch (wParam) {\r
7181     case VK_UP:\r
7182       GetWindowText(hwnd, buf, MSG_SIZ);\r
7183       p = PrevInHistory(buf);\r
7184       if (p != NULL) {\r
7185         SetWindowText(hwnd, p);\r
7186         sel.cpMin = 999999;\r
7187         sel.cpMax = 999999;\r
7188         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7189         return 0;\r
7190       }\r
7191       break;\r
7192     case VK_DOWN:\r
7193       p = NextInHistory();\r
7194       if (p != NULL) {\r
7195         SetWindowText(hwnd, p);\r
7196         sel.cpMin = 999999;\r
7197         sel.cpMax = 999999;\r
7198         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7199         return 0;\r
7200       }\r
7201       break;\r
7202     case VK_HOME:\r
7203     case VK_END:\r
7204       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7205       /* fall thru */\r
7206     case VK_PRIOR:\r
7207     case VK_NEXT:\r
7208       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7209       return 0;\r
7210     }\r
7211     break;\r
7212   case WM_MBUTTONDOWN:\r
7213     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7214       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7215     break;\r
7216   case WM_RBUTTONUP:\r
7217     if (GetKeyState(VK_SHIFT) & ~1) {\r
7218       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7219         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7220     } else {\r
7221       POINT pt;\r
7222       HMENU hmenu;\r
7223       hmenu = LoadMenu(hInst, "InputMenu");\r
7224       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7225       if (sel.cpMin == sel.cpMax) {\r
7226         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7227         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7228       }\r
7229       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7230         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7231       }\r
7232       pt.x = LOWORD(lParam);\r
7233       pt.y = HIWORD(lParam);\r
7234       MenuPopup(hwnd, pt, hmenu, -1);\r
7235     }\r
7236     return 0;\r
7237   case WM_COMMAND:\r
7238     switch (LOWORD(wParam)) { \r
7239     case IDM_Undo:\r
7240       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7241       return 0;\r
7242     case IDM_SelectAll:\r
7243       sel.cpMin = 0;\r
7244       sel.cpMax = -1; /*999999?*/\r
7245       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7246       return 0;\r
7247     case IDM_Cut:\r
7248       SendMessage(hwnd, WM_CUT, 0, 0);\r
7249       return 0;\r
7250     case IDM_Paste:\r
7251       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7252       return 0;\r
7253     case IDM_Copy:\r
7254       SendMessage(hwnd, WM_COPY, 0, 0);\r
7255       return 0;\r
7256     }\r
7257     break;\r
7258   }\r
7259   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7260 }\r
7261 \r
7262 #define CO_MAX  100000\r
7263 #define CO_TRIM   1000\r
7264 \r
7265 LRESULT CALLBACK\r
7266 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7267 {\r
7268   static SnapData sd;\r
7269   HWND hText, hInput;\r
7270   RECT rect;\r
7271   static int sizeX, sizeY;\r
7272   int newSizeX, newSizeY;\r
7273   MINMAXINFO *mmi;\r
7274   WORD wMask;\r
7275 \r
7276   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7277   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7278 \r
7279   switch (message) {\r
7280   case WM_NOTIFY:\r
7281     if (((NMHDR*)lParam)->code == EN_LINK)\r
7282     {\r
7283       ENLINK *pLink = (ENLINK*)lParam;\r
7284       if (pLink->msg == WM_LBUTTONUP)\r
7285       {\r
7286         TEXTRANGE tr;\r
7287 \r
7288         tr.chrg = pLink->chrg;\r
7289         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7290         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7291         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7292         free(tr.lpstrText);\r
7293       }\r
7294     }\r
7295     break;\r
7296   case WM_INITDIALOG: /* message: initialize dialog box */\r
7297     hwndConsole = hDlg;\r
7298     SetFocus(hInput);\r
7299     consoleTextWindowProc = (WNDPROC)\r
7300       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7301     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7302     consoleInputWindowProc = (WNDPROC)\r
7303       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7304     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7305     Colorize(ColorNormal, TRUE);\r
7306     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7307     ChangedConsoleFont();\r
7308     GetClientRect(hDlg, &rect);\r
7309     sizeX = rect.right;\r
7310     sizeY = rect.bottom;\r
7311     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7312         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7313       WINDOWPLACEMENT wp;\r
7314       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7315       wp.length = sizeof(WINDOWPLACEMENT);\r
7316       wp.flags = 0;\r
7317       wp.showCmd = SW_SHOW;\r
7318       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7319       wp.rcNormalPosition.left = wpConsole.x;\r
7320       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7321       wp.rcNormalPosition.top = wpConsole.y;\r
7322       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7323       SetWindowPlacement(hDlg, &wp);\r
7324     }\r
7325 \r
7326    // [HGM] Chessknight's change 2004-07-13\r
7327    else { /* Determine Defaults */\r
7328        WINDOWPLACEMENT wp;\r
7329        wpConsole.x = wpMain.width + 1;\r
7330        wpConsole.y = wpMain.y;\r
7331        wpConsole.width = screenWidth -  wpMain.width;\r
7332        wpConsole.height = wpMain.height;\r
7333        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7334        wp.length = sizeof(WINDOWPLACEMENT);\r
7335        wp.flags = 0;\r
7336        wp.showCmd = SW_SHOW;\r
7337        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7338        wp.rcNormalPosition.left = wpConsole.x;\r
7339        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7340        wp.rcNormalPosition.top = wpConsole.y;\r
7341        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7342        SetWindowPlacement(hDlg, &wp);\r
7343     }\r
7344 \r
7345    // Allow hText to highlight URLs and send notifications on them\r
7346    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7347    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7348    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7349    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7350 \r
7351     return FALSE;\r
7352 \r
7353   case WM_SETFOCUS:\r
7354     SetFocus(hInput);\r
7355     return 0;\r
7356 \r
7357   case WM_CLOSE:\r
7358     ExitEvent(0);\r
7359     /* not reached */\r
7360     break;\r
7361 \r
7362   case WM_SIZE:\r
7363     if (IsIconic(hDlg)) break;\r
7364     newSizeX = LOWORD(lParam);\r
7365     newSizeY = HIWORD(lParam);\r
7366     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7367       RECT rectText, rectInput;\r
7368       POINT pt;\r
7369       int newTextHeight, newTextWidth;\r
7370       GetWindowRect(hText, &rectText);\r
7371       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7372       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7373       if (newTextHeight < 0) {\r
7374         newSizeY += -newTextHeight;\r
7375         newTextHeight = 0;\r
7376       }\r
7377       SetWindowPos(hText, NULL, 0, 0,\r
7378         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7379       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7380       pt.x = rectInput.left;\r
7381       pt.y = rectInput.top + newSizeY - sizeY;\r
7382       ScreenToClient(hDlg, &pt);\r
7383       SetWindowPos(hInput, NULL, \r
7384         pt.x, pt.y, /* needs client coords */   \r
7385         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7386         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7387     }\r
7388     sizeX = newSizeX;\r
7389     sizeY = newSizeY;\r
7390     break;\r
7391 \r
7392   case WM_GETMINMAXINFO:\r
7393     /* Prevent resizing window too small */\r
7394     mmi = (MINMAXINFO *) lParam;\r
7395     mmi->ptMinTrackSize.x = 100;\r
7396     mmi->ptMinTrackSize.y = 100;\r
7397     break;\r
7398 \r
7399   /* [AS] Snapping */\r
7400   case WM_ENTERSIZEMOVE:\r
7401     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7402 \r
7403   case WM_SIZING:\r
7404     return OnSizing( &sd, hDlg, wParam, lParam );\r
7405 \r
7406   case WM_MOVING:\r
7407     return OnMoving( &sd, hDlg, wParam, lParam );\r
7408 \r
7409   case WM_EXITSIZEMOVE:\r
7410         UpdateICSWidth(hText);\r
7411     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7412   }\r
7413 \r
7414   return DefWindowProc(hDlg, message, wParam, lParam);\r
7415 }\r
7416 \r
7417 \r
7418 VOID\r
7419 ConsoleCreate()\r
7420 {\r
7421   HWND hCons;\r
7422   if (hwndConsole) return;\r
7423   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7424   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7425 }\r
7426 \r
7427 \r
7428 VOID\r
7429 ConsoleOutput(char* data, int length, int forceVisible)\r
7430 {\r
7431   HWND hText;\r
7432   int trim, exlen;\r
7433   char *p, *q;\r
7434   char buf[CO_MAX+1];\r
7435   POINT pEnd;\r
7436   RECT rect;\r
7437   static int delayLF = 0;\r
7438   CHARRANGE savesel, sel;\r
7439 \r
7440   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7441   p = data;\r
7442   q = buf;\r
7443   if (delayLF) {\r
7444     *q++ = '\r';\r
7445     *q++ = '\n';\r
7446     delayLF = 0;\r
7447   }\r
7448   while (length--) {\r
7449     if (*p == '\n') {\r
7450       if (*++p) {\r
7451         *q++ = '\r';\r
7452         *q++ = '\n';\r
7453       } else {\r
7454         delayLF = 1;\r
7455       }\r
7456     } else if (*p == '\007') {\r
7457        MyPlaySound(&sounds[(int)SoundBell]);\r
7458        p++;\r
7459     } else {\r
7460       *q++ = *p++;\r
7461     }\r
7462   }\r
7463   *q = NULLCHAR;\r
7464   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7465   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7466   /* Save current selection */\r
7467   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7468   exlen = GetWindowTextLength(hText);\r
7469   /* Find out whether current end of text is visible */\r
7470   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7471   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7472   /* Trim existing text if it's too long */\r
7473   if (exlen + (q - buf) > CO_MAX) {\r
7474     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7475     sel.cpMin = 0;\r
7476     sel.cpMax = trim;\r
7477     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7478     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7479     exlen -= trim;\r
7480     savesel.cpMin -= trim;\r
7481     savesel.cpMax -= trim;\r
7482     if (exlen < 0) exlen = 0;\r
7483     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7484     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7485   }\r
7486   /* Append the new text */\r
7487   sel.cpMin = exlen;\r
7488   sel.cpMax = exlen;\r
7489   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7490   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7491   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7492   if (forceVisible || exlen == 0 ||\r
7493       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7494        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7495     /* Scroll to make new end of text visible if old end of text\r
7496        was visible or new text is an echo of user typein */\r
7497     sel.cpMin = 9999999;\r
7498     sel.cpMax = 9999999;\r
7499     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7500     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7501     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7502     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7503   }\r
7504   if (savesel.cpMax == exlen || forceVisible) {\r
7505     /* Move insert point to new end of text if it was at the old\r
7506        end of text or if the new text is an echo of user typein */\r
7507     sel.cpMin = 9999999;\r
7508     sel.cpMax = 9999999;\r
7509     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7510   } else {\r
7511     /* Restore previous selection */\r
7512     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7513   }\r
7514   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7515 }\r
7516 \r
7517 /*---------*/\r
7518 \r
7519 \r
7520 void\r
7521 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7522 {\r
7523   char buf[100];\r
7524   char *str;\r
7525   COLORREF oldFg, oldBg;\r
7526   HFONT oldFont;\r
7527   RECT rect;\r
7528 \r
7529   if(copyNumber > 1)\r
7530     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7531 \r
7532   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7533   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7534   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7535 \r
7536   rect.left = x;\r
7537   rect.right = x + squareSize;\r
7538   rect.top  = y;\r
7539   rect.bottom = y + squareSize;\r
7540   str = buf;\r
7541 \r
7542   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7543                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7544              y, ETO_CLIPPED|ETO_OPAQUE,\r
7545              &rect, str, strlen(str), NULL);\r
7546 \r
7547   (void) SetTextColor(hdc, oldFg);\r
7548   (void) SetBkColor(hdc, oldBg);\r
7549   (void) SelectObject(hdc, oldFont);\r
7550 }\r
7551 \r
7552 void\r
7553 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7554               RECT *rect, char *color, char *flagFell)\r
7555 {\r
7556   char buf[100];\r
7557   char *str;\r
7558   COLORREF oldFg, oldBg;\r
7559   HFONT oldFont;\r
7560 \r
7561   if (twoBoards && partnerUp) return;\r
7562   if (appData.clockMode) {\r
7563     if (tinyLayout)\r
7564       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7565     else\r
7566       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7567     str = buf;\r
7568   } else {\r
7569     str = color;\r
7570   }\r
7571 \r
7572   if (highlight) {\r
7573     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7574     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7575   } else {\r
7576     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7577     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7578   }\r
7579   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7580 \r
7581   JAWS_SILENCE\r
7582 \r
7583   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7584              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7585              rect, str, strlen(str), NULL);\r
7586   if(logoHeight > 0 && appData.clockMode) {\r
7587       RECT r;\r
7588       str += strlen(color)+2;\r
7589       r.top = rect->top + logoHeight/2;\r
7590       r.left = rect->left;\r
7591       r.right = rect->right;\r
7592       r.bottom = rect->bottom;\r
7593       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7594                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7595                  &r, str, strlen(str), NULL);\r
7596   }\r
7597   (void) SetTextColor(hdc, oldFg);\r
7598   (void) SetBkColor(hdc, oldBg);\r
7599   (void) SelectObject(hdc, oldFont);\r
7600 }\r
7601 \r
7602 \r
7603 int\r
7604 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7605            OVERLAPPED *ovl)\r
7606 {\r
7607   int ok, err;\r
7608 \r
7609   /* [AS]  */\r
7610   if( count <= 0 ) {\r
7611     if (appData.debugMode) {\r
7612       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7613     }\r
7614 \r
7615     return ERROR_INVALID_USER_BUFFER;\r
7616   }\r
7617 \r
7618   ResetEvent(ovl->hEvent);\r
7619   ovl->Offset = ovl->OffsetHigh = 0;\r
7620   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7621   if (ok) {\r
7622     err = NO_ERROR;\r
7623   } else {\r
7624     err = GetLastError();\r
7625     if (err == ERROR_IO_PENDING) {\r
7626       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7627       if (ok)\r
7628         err = NO_ERROR;\r
7629       else\r
7630         err = GetLastError();\r
7631     }\r
7632   }\r
7633   return err;\r
7634 }\r
7635 \r
7636 int\r
7637 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7638             OVERLAPPED *ovl)\r
7639 {\r
7640   int ok, err;\r
7641 \r
7642   ResetEvent(ovl->hEvent);\r
7643   ovl->Offset = ovl->OffsetHigh = 0;\r
7644   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7645   if (ok) {\r
7646     err = NO_ERROR;\r
7647   } else {\r
7648     err = GetLastError();\r
7649     if (err == ERROR_IO_PENDING) {\r
7650       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7651       if (ok)\r
7652         err = NO_ERROR;\r
7653       else\r
7654         err = GetLastError();\r
7655     }\r
7656   }\r
7657   return err;\r
7658 }\r
7659 \r
7660 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7661 void CheckForInputBufferFull( InputSource * is )\r
7662 {\r
7663     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7664         /* Look for end of line */\r
7665         char * p = is->buf;\r
7666         \r
7667         while( p < is->next && *p != '\n' ) {\r
7668             p++;\r
7669         }\r
7670 \r
7671         if( p >= is->next ) {\r
7672             if (appData.debugMode) {\r
7673                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7674             }\r
7675 \r
7676             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7677             is->count = (DWORD) -1;\r
7678             is->next = is->buf;\r
7679         }\r
7680     }\r
7681 }\r
7682 \r
7683 DWORD\r
7684 InputThread(LPVOID arg)\r
7685 {\r
7686   InputSource *is;\r
7687   OVERLAPPED ovl;\r
7688 \r
7689   is = (InputSource *) arg;\r
7690   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7691   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7692   while (is->hThread != NULL) {\r
7693     is->error = DoReadFile(is->hFile, is->next,\r
7694                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7695                            &is->count, &ovl);\r
7696     if (is->error == NO_ERROR) {\r
7697       is->next += is->count;\r
7698     } else {\r
7699       if (is->error == ERROR_BROKEN_PIPE) {\r
7700         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7701         is->count = 0;\r
7702       } else {\r
7703         is->count = (DWORD) -1;\r
7704         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7705         break; \r
7706       }\r
7707     }\r
7708 \r
7709     CheckForInputBufferFull( is );\r
7710 \r
7711     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7712 \r
7713     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7714 \r
7715     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7716   }\r
7717 \r
7718   CloseHandle(ovl.hEvent);\r
7719   CloseHandle(is->hFile);\r
7720 \r
7721   if (appData.debugMode) {\r
7722     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7723   }\r
7724 \r
7725   return 0;\r
7726 }\r
7727 \r
7728 \r
7729 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7730 DWORD\r
7731 NonOvlInputThread(LPVOID arg)\r
7732 {\r
7733   InputSource *is;\r
7734   char *p, *q;\r
7735   int i;\r
7736   char prev;\r
7737 \r
7738   is = (InputSource *) arg;\r
7739   while (is->hThread != NULL) {\r
7740     is->error = ReadFile(is->hFile, is->next,\r
7741                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7742                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7743     if (is->error == NO_ERROR) {\r
7744       /* Change CRLF to LF */\r
7745       if (is->next > is->buf) {\r
7746         p = is->next - 1;\r
7747         i = is->count + 1;\r
7748       } else {\r
7749         p = is->next;\r
7750         i = is->count;\r
7751       }\r
7752       q = p;\r
7753       prev = NULLCHAR;\r
7754       while (i > 0) {\r
7755         if (prev == '\r' && *p == '\n') {\r
7756           *(q-1) = '\n';\r
7757           is->count--;\r
7758         } else { \r
7759           *q++ = *p;\r
7760         }\r
7761         prev = *p++;\r
7762         i--;\r
7763       }\r
7764       *q = NULLCHAR;\r
7765       is->next = q;\r
7766     } else {\r
7767       if (is->error == ERROR_BROKEN_PIPE) {\r
7768         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7769         is->count = 0; \r
7770       } else {\r
7771         is->count = (DWORD) -1;\r
7772       }\r
7773     }\r
7774 \r
7775     CheckForInputBufferFull( is );\r
7776 \r
7777     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7778 \r
7779     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7780 \r
7781     if (is->count < 0) break;  /* Quit on error */\r
7782   }\r
7783   CloseHandle(is->hFile);\r
7784   return 0;\r
7785 }\r
7786 \r
7787 DWORD\r
7788 SocketInputThread(LPVOID arg)\r
7789 {\r
7790   InputSource *is;\r
7791 \r
7792   is = (InputSource *) arg;\r
7793   while (is->hThread != NULL) {\r
7794     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7795     if ((int)is->count == SOCKET_ERROR) {\r
7796       is->count = (DWORD) -1;\r
7797       is->error = WSAGetLastError();\r
7798     } else {\r
7799       is->error = NO_ERROR;\r
7800       is->next += is->count;\r
7801       if (is->count == 0 && is->second == is) {\r
7802         /* End of file on stderr; quit with no message */\r
7803         break;\r
7804       }\r
7805     }\r
7806     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7807 \r
7808     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7809 \r
7810     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7811   }\r
7812   return 0;\r
7813 }\r
7814 \r
7815 VOID\r
7816 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7817 {\r
7818   InputSource *is;\r
7819 \r
7820   is = (InputSource *) lParam;\r
7821   if (is->lineByLine) {\r
7822     /* Feed in lines one by one */\r
7823     char *p = is->buf;\r
7824     char *q = p;\r
7825     while (q < is->next) {\r
7826       if (*q++ == '\n') {\r
7827         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7828         p = q;\r
7829       }\r
7830     }\r
7831     \r
7832     /* Move any partial line to the start of the buffer */\r
7833     q = is->buf;\r
7834     while (p < is->next) {\r
7835       *q++ = *p++;\r
7836     }\r
7837     is->next = q;\r
7838 \r
7839     if (is->error != NO_ERROR || is->count == 0) {\r
7840       /* Notify backend of the error.  Note: If there was a partial\r
7841          line at the end, it is not flushed through. */\r
7842       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7843     }\r
7844   } else {\r
7845     /* Feed in the whole chunk of input at once */\r
7846     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7847     is->next = is->buf;\r
7848   }\r
7849 }\r
7850 \r
7851 /*---------------------------------------------------------------------------*\\r
7852  *\r
7853  *  Menu enables. Used when setting various modes.\r
7854  *\r
7855 \*---------------------------------------------------------------------------*/\r
7856 \r
7857 typedef struct {\r
7858   int item;\r
7859   int flags;\r
7860 } Enables;\r
7861 \r
7862 VOID\r
7863 GreyRevert(Boolean grey)\r
7864 { // [HGM] vari: for retracting variations in local mode\r
7865   HMENU hmenu = GetMenu(hwndMain);\r
7866   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7867   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7868 }\r
7869 \r
7870 VOID\r
7871 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7872 {\r
7873   while (enab->item > 0) {\r
7874     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7875     enab++;\r
7876   }\r
7877 }\r
7878 \r
7879 Enables gnuEnables[] = {\r
7880   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7893 \r
7894   // Needed to switch from ncp to GNU mode on Engine Load\r
7895   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7896   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7897   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7898   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7899   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7900   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7901   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7909   { -1, -1 }\r
7910 };\r
7911 \r
7912 Enables icsEnables[] = {\r
7913   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7925   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7927   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7929   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7933   { -1, -1 }\r
7934 };\r
7935 \r
7936 #if ZIPPY\r
7937 Enables zippyEnables[] = {\r
7938   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7939   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7940   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7941   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7942   { -1, -1 }\r
7943 };\r
7944 #endif\r
7945 \r
7946 Enables ncpEnables[] = {\r
7947   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7956   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7957   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7969   { -1, -1 }\r
7970 };\r
7971 \r
7972 Enables trainingOnEnables[] = {\r
7973   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7981   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7982   { -1, -1 }\r
7983 };\r
7984 \r
7985 Enables trainingOffEnables[] = {\r
7986   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7989   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7990   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7991   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7992   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7993   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7994   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7995   { -1, -1 }\r
7996 };\r
7997 \r
7998 /* These modify either ncpEnables or gnuEnables */\r
7999 Enables cmailEnables[] = {\r
8000   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8001   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8002   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8003   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8004   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8005   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8007   { -1, -1 }\r
8008 };\r
8009 \r
8010 Enables machineThinkingEnables[] = {\r
8011   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8016   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8017   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8018   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8019   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8020   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8024 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8027   { -1, -1 }\r
8028 };\r
8029 \r
8030 Enables userThinkingEnables[] = {\r
8031   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8032   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8033   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8034   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8035   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8042   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8043   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8044 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8045   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8046   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8047   { -1, -1 }\r
8048 };\r
8049 \r
8050 /*---------------------------------------------------------------------------*\\r
8051  *\r
8052  *  Front-end interface functions exported by XBoard.\r
8053  *  Functions appear in same order as prototypes in frontend.h.\r
8054  * \r
8055 \*---------------------------------------------------------------------------*/\r
8056 VOID\r
8057 CheckMark(UINT item, int state)\r
8058 {\r
8059     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8060 }\r
8061 \r
8062 VOID\r
8063 ModeHighlight()\r
8064 {\r
8065   static UINT prevChecked = 0;\r
8066   static int prevPausing = 0;\r
8067   UINT nowChecked;\r
8068 \r
8069   if (pausing != prevPausing) {\r
8070     prevPausing = pausing;\r
8071     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8072                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8073     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8074   }\r
8075 \r
8076   switch (gameMode) {\r
8077   case BeginningOfGame:\r
8078     if (appData.icsActive)\r
8079       nowChecked = IDM_IcsClient;\r
8080     else if (appData.noChessProgram)\r
8081       nowChecked = IDM_EditGame;\r
8082     else\r
8083       nowChecked = IDM_MachineBlack;\r
8084     break;\r
8085   case MachinePlaysBlack:\r
8086     nowChecked = IDM_MachineBlack;\r
8087     break;\r
8088   case MachinePlaysWhite:\r
8089     nowChecked = IDM_MachineWhite;\r
8090     break;\r
8091   case TwoMachinesPlay:\r
8092     nowChecked = IDM_TwoMachines;\r
8093     break;\r
8094   case AnalyzeMode:\r
8095     nowChecked = IDM_AnalysisMode;\r
8096     break;\r
8097   case AnalyzeFile:\r
8098     nowChecked = IDM_AnalyzeFile;\r
8099     break;\r
8100   case EditGame:\r
8101     nowChecked = IDM_EditGame;\r
8102     break;\r
8103   case PlayFromGameFile:\r
8104     nowChecked = IDM_LoadGame;\r
8105     break;\r
8106   case EditPosition:\r
8107     nowChecked = IDM_EditPosition;\r
8108     break;\r
8109   case Training:\r
8110     nowChecked = IDM_Training;\r
8111     break;\r
8112   case IcsPlayingWhite:\r
8113   case IcsPlayingBlack:\r
8114   case IcsObserving:\r
8115   case IcsIdle:\r
8116     nowChecked = IDM_IcsClient;\r
8117     break;\r
8118   default:\r
8119   case EndOfGame:\r
8120     nowChecked = 0;\r
8121     break;\r
8122   }\r
8123   CheckMark(prevChecked, MF_UNCHECKED);\r
8124   CheckMark(nowChecked, MF_CHECKED);\r
8125   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8126 \r
8127   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8128     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8129                           MF_BYCOMMAND|MF_ENABLED);\r
8130   } else {\r
8131     (void) EnableMenuItem(GetMenu(hwndMain), \r
8132                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8133   }\r
8134 \r
8135   prevChecked = nowChecked;\r
8136 \r
8137   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8138   if (appData.icsActive) {\r
8139        if (appData.icsEngineAnalyze) {\r
8140                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8141        } else {\r
8142                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8143        }\r
8144   }\r
8145   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8146 }\r
8147 \r
8148 VOID\r
8149 SetICSMode()\r
8150 {\r
8151   HMENU hmenu = GetMenu(hwndMain);\r
8152   SetMenuEnables(hmenu, icsEnables);\r
8153   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8154     MF_BYCOMMAND|MF_ENABLED);\r
8155 #if ZIPPY\r
8156   if (appData.zippyPlay) {\r
8157     SetMenuEnables(hmenu, zippyEnables);\r
8158     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8159          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8160           MF_BYCOMMAND|MF_ENABLED);\r
8161   }\r
8162 #endif\r
8163 }\r
8164 \r
8165 VOID\r
8166 SetGNUMode()\r
8167 {\r
8168   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8169 }\r
8170 \r
8171 VOID\r
8172 SetNCPMode()\r
8173 {\r
8174   HMENU hmenu = GetMenu(hwndMain);\r
8175   SetMenuEnables(hmenu, ncpEnables);\r
8176     DrawMenuBar(hwndMain);\r
8177 }\r
8178 \r
8179 VOID\r
8180 SetCmailMode()\r
8181 {\r
8182   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8183 }\r
8184 \r
8185 VOID \r
8186 SetTrainingModeOn()\r
8187 {\r
8188   int i;\r
8189   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8190   for (i = 0; i < N_BUTTONS; i++) {\r
8191     if (buttonDesc[i].hwnd != NULL)\r
8192       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8193   }\r
8194   CommentPopDown();\r
8195 }\r
8196 \r
8197 VOID SetTrainingModeOff()\r
8198 {\r
8199   int i;\r
8200   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8201   for (i = 0; i < N_BUTTONS; i++) {\r
8202     if (buttonDesc[i].hwnd != NULL)\r
8203       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8204   }\r
8205 }\r
8206 \r
8207 \r
8208 VOID\r
8209 SetUserThinkingEnables()\r
8210 {\r
8211   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8212 }\r
8213 \r
8214 VOID\r
8215 SetMachineThinkingEnables()\r
8216 {\r
8217   HMENU hMenu = GetMenu(hwndMain);\r
8218   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8219 \r
8220   SetMenuEnables(hMenu, machineThinkingEnables);\r
8221 \r
8222   if (gameMode == MachinePlaysBlack) {\r
8223     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8224   } else if (gameMode == MachinePlaysWhite) {\r
8225     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8226   } else if (gameMode == TwoMachinesPlay) {\r
8227     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8228   }\r
8229 }\r
8230 \r
8231 \r
8232 VOID\r
8233 DisplayTitle(char *str)\r
8234 {\r
8235   char title[MSG_SIZ], *host;\r
8236   if (str[0] != NULLCHAR) {\r
8237     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8238   } else if (appData.icsActive) {\r
8239     if (appData.icsCommPort[0] != NULLCHAR)\r
8240       host = "ICS";\r
8241     else \r
8242       host = appData.icsHost;\r
8243       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8244   } else if (appData.noChessProgram) {\r
8245     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8246   } else {\r
8247     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8248     strcat(title, ": ");\r
8249     strcat(title, first.tidy);\r
8250   }\r
8251   SetWindowText(hwndMain, title);\r
8252 }\r
8253 \r
8254 \r
8255 VOID\r
8256 DisplayMessage(char *str1, char *str2)\r
8257 {\r
8258   HDC hdc;\r
8259   HFONT oldFont;\r
8260   int remain = MESSAGE_TEXT_MAX - 1;\r
8261   int len;\r
8262 \r
8263   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8264   messageText[0] = NULLCHAR;\r
8265   if (*str1) {\r
8266     len = strlen(str1);\r
8267     if (len > remain) len = remain;\r
8268     strncpy(messageText, str1, len);\r
8269     messageText[len] = NULLCHAR;\r
8270     remain -= len;\r
8271   }\r
8272   if (*str2 && remain >= 2) {\r
8273     if (*str1) {\r
8274       strcat(messageText, "  ");\r
8275       remain -= 2;\r
8276     }\r
8277     len = strlen(str2);\r
8278     if (len > remain) len = remain;\r
8279     strncat(messageText, str2, len);\r
8280   }\r
8281   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8282   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8283 \r
8284   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8285 \r
8286   SAYMACHINEMOVE();\r
8287 \r
8288   hdc = GetDC(hwndMain);\r
8289   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8290   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8291              &messageRect, messageText, strlen(messageText), NULL);\r
8292   (void) SelectObject(hdc, oldFont);\r
8293   (void) ReleaseDC(hwndMain, hdc);\r
8294 }\r
8295 \r
8296 VOID\r
8297 DisplayError(char *str, int error)\r
8298 {\r
8299   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8300   int len;\r
8301 \r
8302   if (error == 0) {\r
8303     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8304   } else {\r
8305     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8306                         NULL, error, LANG_NEUTRAL,\r
8307                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8308     if (len > 0) {\r
8309       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8310     } else {\r
8311       ErrorMap *em = errmap;\r
8312       while (em->err != 0 && em->err != error) em++;\r
8313       if (em->err != 0) {\r
8314         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8315       } else {\r
8316         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8317       }\r
8318     }\r
8319   }\r
8320   \r
8321   ErrorPopUp(_("Error"), buf);\r
8322 }\r
8323 \r
8324 \r
8325 VOID\r
8326 DisplayMoveError(char *str)\r
8327 {\r
8328   fromX = fromY = -1;\r
8329   ClearHighlights();\r
8330   DrawPosition(FALSE, NULL);\r
8331   if (appData.popupMoveErrors) {\r
8332     ErrorPopUp(_("Error"), str);\r
8333   } else {\r
8334     DisplayMessage(str, "");\r
8335     moveErrorMessageUp = TRUE;\r
8336   }\r
8337 }\r
8338 \r
8339 VOID\r
8340 DisplayFatalError(char *str, int error, int exitStatus)\r
8341 {\r
8342   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8343   int len;\r
8344   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8345 \r
8346   if (error != 0) {\r
8347     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8348                         NULL, error, LANG_NEUTRAL,\r
8349                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8350     if (len > 0) {\r
8351       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8352     } else {\r
8353       ErrorMap *em = errmap;\r
8354       while (em->err != 0 && em->err != error) em++;\r
8355       if (em->err != 0) {\r
8356         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8357       } else {\r
8358         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8359       }\r
8360     }\r
8361     str = buf;\r
8362   }\r
8363   if (appData.debugMode) {\r
8364     fprintf(debugFP, "%s: %s\n", label, str);\r
8365   }\r
8366   if (appData.popupExitMessage) {\r
8367     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8368                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8369   }\r
8370   ExitEvent(exitStatus);\r
8371 }\r
8372 \r
8373 \r
8374 VOID\r
8375 DisplayInformation(char *str)\r
8376 {\r
8377   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8378 }\r
8379 \r
8380 \r
8381 VOID\r
8382 DisplayNote(char *str)\r
8383 {\r
8384   ErrorPopUp(_("Note"), str);\r
8385 }\r
8386 \r
8387 \r
8388 typedef struct {\r
8389   char *title, *question, *replyPrefix;\r
8390   ProcRef pr;\r
8391 } QuestionParams;\r
8392 \r
8393 LRESULT CALLBACK\r
8394 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8395 {\r
8396   static QuestionParams *qp;\r
8397   char reply[MSG_SIZ];\r
8398   int len, err;\r
8399 \r
8400   switch (message) {\r
8401   case WM_INITDIALOG:\r
8402     qp = (QuestionParams *) lParam;\r
8403     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8404     Translate(hDlg, DLG_Question);\r
8405     SetWindowText(hDlg, qp->title);\r
8406     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8407     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8408     return FALSE;\r
8409 \r
8410   case WM_COMMAND:\r
8411     switch (LOWORD(wParam)) {\r
8412     case IDOK:\r
8413       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8414       if (*reply) strcat(reply, " ");\r
8415       len = strlen(reply);\r
8416       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8417       strcat(reply, "\n");\r
8418       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8419       EndDialog(hDlg, TRUE);\r
8420       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8421       return TRUE;\r
8422     case IDCANCEL:\r
8423       EndDialog(hDlg, FALSE);\r
8424       return TRUE;\r
8425     default:\r
8426       break;\r
8427     }\r
8428     break;\r
8429   }\r
8430   return FALSE;\r
8431 }\r
8432 \r
8433 VOID\r
8434 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8435 {\r
8436     QuestionParams qp;\r
8437     FARPROC lpProc;\r
8438     \r
8439     qp.title = title;\r
8440     qp.question = question;\r
8441     qp.replyPrefix = replyPrefix;\r
8442     qp.pr = pr;\r
8443     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8444     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8445       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8446     FreeProcInstance(lpProc);\r
8447 }\r
8448 \r
8449 /* [AS] Pick FRC position */\r
8450 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8451 {\r
8452     static int * lpIndexFRC;\r
8453     BOOL index_is_ok;\r
8454     char buf[16];\r
8455 \r
8456     switch( message )\r
8457     {\r
8458     case WM_INITDIALOG:\r
8459         lpIndexFRC = (int *) lParam;\r
8460 \r
8461         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8462         Translate(hDlg, DLG_NewGameFRC);\r
8463 \r
8464         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8465         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8466         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8467         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8468 \r
8469         break;\r
8470 \r
8471     case WM_COMMAND:\r
8472         switch( LOWORD(wParam) ) {\r
8473         case IDOK:\r
8474             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8475             EndDialog( hDlg, 0 );\r
8476             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8477             return TRUE;\r
8478         case IDCANCEL:\r
8479             EndDialog( hDlg, 1 );   \r
8480             return TRUE;\r
8481         case IDC_NFG_Edit:\r
8482             if( HIWORD(wParam) == EN_CHANGE ) {\r
8483                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8484 \r
8485                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8486             }\r
8487             return TRUE;\r
8488         case IDC_NFG_Random:\r
8489           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8490             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8491             return TRUE;\r
8492         }\r
8493 \r
8494         break;\r
8495     }\r
8496 \r
8497     return FALSE;\r
8498 }\r
8499 \r
8500 int NewGameFRC()\r
8501 {\r
8502     int result;\r
8503     int index = appData.defaultFrcPosition;\r
8504     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8505 \r
8506     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8507 \r
8508     if( result == 0 ) {\r
8509         appData.defaultFrcPosition = index;\r
8510     }\r
8511 \r
8512     return result;\r
8513 }\r
8514 \r
8515 /* [AS] Game list options. Refactored by HGM */\r
8516 \r
8517 HWND gameListOptionsDialog;\r
8518 \r
8519 // low-level front-end: clear text edit / list widget\r
8520 void\r
8521 GLT_ClearList()\r
8522 {\r
8523     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8524 }\r
8525 \r
8526 // low-level front-end: clear text edit / list widget\r
8527 void\r
8528 GLT_DeSelectList()\r
8529 {\r
8530     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8531 }\r
8532 \r
8533 // low-level front-end: append line to text edit / list widget\r
8534 void\r
8535 GLT_AddToList( char *name )\r
8536 {\r
8537     if( name != 0 ) {\r
8538             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8539     }\r
8540 }\r
8541 \r
8542 // low-level front-end: get line from text edit / list widget\r
8543 Boolean\r
8544 GLT_GetFromList( int index, char *name )\r
8545 {\r
8546     if( name != 0 ) {\r
8547             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8548                 return TRUE;\r
8549     }\r
8550     return FALSE;\r
8551 }\r
8552 \r
8553 void GLT_MoveSelection( HWND hDlg, int delta )\r
8554 {\r
8555     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8556     int idx2 = idx1 + delta;\r
8557     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8558 \r
8559     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8560         char buf[128];\r
8561 \r
8562         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8563         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8564         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8565         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8566     }\r
8567 }\r
8568 \r
8569 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8570 {\r
8571     switch( message )\r
8572     {\r
8573     case WM_INITDIALOG:\r
8574         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8575         \r
8576         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8577         Translate(hDlg, DLG_GameListOptions);\r
8578 \r
8579         /* Initialize list */\r
8580         GLT_TagsToList( lpUserGLT );\r
8581 \r
8582         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8583 \r
8584         break;\r
8585 \r
8586     case WM_COMMAND:\r
8587         switch( LOWORD(wParam) ) {\r
8588         case IDOK:\r
8589             GLT_ParseList();\r
8590             EndDialog( hDlg, 0 );\r
8591             return TRUE;\r
8592         case IDCANCEL:\r
8593             EndDialog( hDlg, 1 );\r
8594             return TRUE;\r
8595 \r
8596         case IDC_GLT_Default:\r
8597             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8598             return TRUE;\r
8599 \r
8600         case IDC_GLT_Restore:\r
8601             GLT_TagsToList( appData.gameListTags );\r
8602             return TRUE;\r
8603 \r
8604         case IDC_GLT_Up:\r
8605             GLT_MoveSelection( hDlg, -1 );\r
8606             return TRUE;\r
8607 \r
8608         case IDC_GLT_Down:\r
8609             GLT_MoveSelection( hDlg, +1 );\r
8610             return TRUE;\r
8611         }\r
8612 \r
8613         break;\r
8614     }\r
8615 \r
8616     return FALSE;\r
8617 }\r
8618 \r
8619 int GameListOptions()\r
8620 {\r
8621     int result;\r
8622     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8623 \r
8624       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8625 \r
8626     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8627 \r
8628     if( result == 0 ) {\r
8629         /* [AS] Memory leak here! */\r
8630         appData.gameListTags = strdup( lpUserGLT ); \r
8631     }\r
8632 \r
8633     return result;\r
8634 }\r
8635 \r
8636 VOID\r
8637 DisplayIcsInteractionTitle(char *str)\r
8638 {\r
8639   char consoleTitle[MSG_SIZ];\r
8640 \r
8641     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8642     SetWindowText(hwndConsole, consoleTitle);\r
8643 \r
8644     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8645       char buf[MSG_SIZ], *p = buf, *q;\r
8646         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8647       do {\r
8648         q = strchr(p, ';');\r
8649         if(q) *q++ = 0;\r
8650         if(*p) ChatPopUp(p);\r
8651       } while(p=q);\r
8652     }\r
8653 \r
8654     SetActiveWindow(hwndMain);\r
8655 }\r
8656 \r
8657 void\r
8658 DrawPosition(int fullRedraw, Board board)\r
8659 {\r
8660   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8661 }\r
8662 \r
8663 void NotifyFrontendLogin()\r
8664 {\r
8665         if (hwndConsole)\r
8666                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8667 }\r
8668 \r
8669 VOID\r
8670 ResetFrontEnd()\r
8671 {\r
8672   fromX = fromY = -1;\r
8673   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8674     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8675     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8676     dragInfo.lastpos = dragInfo.pos;\r
8677     dragInfo.start.x = dragInfo.start.y = -1;\r
8678     dragInfo.from = dragInfo.start;\r
8679     ReleaseCapture();\r
8680     DrawPosition(TRUE, NULL);\r
8681   }\r
8682   TagsPopDown();\r
8683 }\r
8684 \r
8685 \r
8686 VOID\r
8687 CommentPopUp(char *title, char *str)\r
8688 {\r
8689   HWND hwnd = GetActiveWindow();\r
8690   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8691   SAY(str);\r
8692   SetActiveWindow(hwnd);\r
8693 }\r
8694 \r
8695 VOID\r
8696 CommentPopDown(void)\r
8697 {\r
8698   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8699   if (commentDialog) {\r
8700     ShowWindow(commentDialog, SW_HIDE);\r
8701   }\r
8702   commentUp = FALSE;\r
8703 }\r
8704 \r
8705 VOID\r
8706 EditCommentPopUp(int index, char *title, char *str)\r
8707 {\r
8708   EitherCommentPopUp(index, title, str, TRUE);\r
8709 }\r
8710 \r
8711 \r
8712 VOID\r
8713 RingBell()\r
8714 {\r
8715   MyPlaySound(&sounds[(int)SoundMove]);\r
8716 }\r
8717 \r
8718 VOID PlayIcsWinSound()\r
8719 {\r
8720   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8721 }\r
8722 \r
8723 VOID PlayIcsLossSound()\r
8724 {\r
8725   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8726 }\r
8727 \r
8728 VOID PlayIcsDrawSound()\r
8729 {\r
8730   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8731 }\r
8732 \r
8733 VOID PlayIcsUnfinishedSound()\r
8734 {\r
8735   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8736 }\r
8737 \r
8738 VOID\r
8739 PlayAlarmSound()\r
8740 {\r
8741   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8742 }\r
8743 \r
8744 VOID\r
8745 PlayTellSound()\r
8746 {\r
8747   MyPlaySound(&textAttribs[ColorTell].sound);\r
8748 }\r
8749 \r
8750 \r
8751 VOID\r
8752 EchoOn()\r
8753 {\r
8754   HWND hInput;\r
8755   consoleEcho = TRUE;\r
8756   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8757   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8758   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8759 }\r
8760 \r
8761 \r
8762 VOID\r
8763 EchoOff()\r
8764 {\r
8765   CHARFORMAT cf;\r
8766   HWND hInput;\r
8767   consoleEcho = FALSE;\r
8768   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8769   /* This works OK: set text and background both to the same color */\r
8770   cf = consoleCF;\r
8771   cf.crTextColor = COLOR_ECHOOFF;\r
8772   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8773   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8774 }\r
8775 \r
8776 /* No Raw()...? */\r
8777 \r
8778 void Colorize(ColorClass cc, int continuation)\r
8779 {\r
8780   currentColorClass = cc;\r
8781   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8782   consoleCF.crTextColor = textAttribs[cc].color;\r
8783   consoleCF.dwEffects = textAttribs[cc].effects;\r
8784   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8785 }\r
8786 \r
8787 char *\r
8788 UserName()\r
8789 {\r
8790   static char buf[MSG_SIZ];\r
8791   DWORD bufsiz = MSG_SIZ;\r
8792 \r
8793   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8794         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8795   }\r
8796   if (!GetUserName(buf, &bufsiz)) {\r
8797     /*DisplayError("Error getting user name", GetLastError());*/\r
8798     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8799   }\r
8800   return buf;\r
8801 }\r
8802 \r
8803 char *\r
8804 HostName()\r
8805 {\r
8806   static char buf[MSG_SIZ];\r
8807   DWORD bufsiz = MSG_SIZ;\r
8808 \r
8809   if (!GetComputerName(buf, &bufsiz)) {\r
8810     /*DisplayError("Error getting host name", GetLastError());*/\r
8811     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8812   }\r
8813   return buf;\r
8814 }\r
8815 \r
8816 \r
8817 int\r
8818 ClockTimerRunning()\r
8819 {\r
8820   return clockTimerEvent != 0;\r
8821 }\r
8822 \r
8823 int\r
8824 StopClockTimer()\r
8825 {\r
8826   if (clockTimerEvent == 0) return FALSE;\r
8827   KillTimer(hwndMain, clockTimerEvent);\r
8828   clockTimerEvent = 0;\r
8829   return TRUE;\r
8830 }\r
8831 \r
8832 void\r
8833 StartClockTimer(long millisec)\r
8834 {\r
8835   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8836                              (UINT) millisec, NULL);\r
8837 }\r
8838 \r
8839 void\r
8840 DisplayWhiteClock(long timeRemaining, int highlight)\r
8841 {\r
8842   HDC hdc;\r
8843   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8844 \r
8845   if(appData.noGUI) return;\r
8846   hdc = GetDC(hwndMain);\r
8847   if (!IsIconic(hwndMain)) {\r
8848     DisplayAClock(hdc, timeRemaining, highlight, \r
8849                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8850   }\r
8851   if (highlight && iconCurrent == iconBlack) {\r
8852     iconCurrent = iconWhite;\r
8853     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8854     if (IsIconic(hwndMain)) {\r
8855       DrawIcon(hdc, 2, 2, iconCurrent);\r
8856     }\r
8857   }\r
8858   (void) ReleaseDC(hwndMain, hdc);\r
8859   if (hwndConsole)\r
8860     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8861 }\r
8862 \r
8863 void\r
8864 DisplayBlackClock(long timeRemaining, int highlight)\r
8865 {\r
8866   HDC hdc;\r
8867   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8868 \r
8869   if(appData.noGUI) return;\r
8870   hdc = GetDC(hwndMain);\r
8871   if (!IsIconic(hwndMain)) {\r
8872     DisplayAClock(hdc, timeRemaining, highlight, \r
8873                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8874   }\r
8875   if (highlight && iconCurrent == iconWhite) {\r
8876     iconCurrent = iconBlack;\r
8877     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8878     if (IsIconic(hwndMain)) {\r
8879       DrawIcon(hdc, 2, 2, iconCurrent);\r
8880     }\r
8881   }\r
8882   (void) ReleaseDC(hwndMain, hdc);\r
8883   if (hwndConsole)\r
8884     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8885 }\r
8886 \r
8887 \r
8888 int\r
8889 LoadGameTimerRunning()\r
8890 {\r
8891   return loadGameTimerEvent != 0;\r
8892 }\r
8893 \r
8894 int\r
8895 StopLoadGameTimer()\r
8896 {\r
8897   if (loadGameTimerEvent == 0) return FALSE;\r
8898   KillTimer(hwndMain, loadGameTimerEvent);\r
8899   loadGameTimerEvent = 0;\r
8900   return TRUE;\r
8901 }\r
8902 \r
8903 void\r
8904 StartLoadGameTimer(long millisec)\r
8905 {\r
8906   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8907                                 (UINT) millisec, NULL);\r
8908 }\r
8909 \r
8910 void\r
8911 AutoSaveGame()\r
8912 {\r
8913   char *defName;\r
8914   FILE *f;\r
8915   char fileTitle[MSG_SIZ];\r
8916 \r
8917   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8918   f = OpenFileDialog(hwndMain, "a", defName,\r
8919                      appData.oldSaveStyle ? "gam" : "pgn",\r
8920                      GAME_FILT, \r
8921                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8922   if (f != NULL) {\r
8923     SaveGame(f, 0, "");\r
8924     fclose(f);\r
8925   }\r
8926 }\r
8927 \r
8928 \r
8929 void\r
8930 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8931 {\r
8932   if (delayedTimerEvent != 0) {\r
8933     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8934       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8935     }\r
8936     KillTimer(hwndMain, delayedTimerEvent);\r
8937     delayedTimerEvent = 0;\r
8938     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8939     delayedTimerCallback();\r
8940   }\r
8941   delayedTimerCallback = cb;\r
8942   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8943                                 (UINT) millisec, NULL);\r
8944 }\r
8945 \r
8946 DelayedEventCallback\r
8947 GetDelayedEvent()\r
8948 {\r
8949   if (delayedTimerEvent) {\r
8950     return delayedTimerCallback;\r
8951   } else {\r
8952     return NULL;\r
8953   }\r
8954 }\r
8955 \r
8956 void\r
8957 CancelDelayedEvent()\r
8958 {\r
8959   if (delayedTimerEvent) {\r
8960     KillTimer(hwndMain, delayedTimerEvent);\r
8961     delayedTimerEvent = 0;\r
8962   }\r
8963 }\r
8964 \r
8965 DWORD GetWin32Priority(int nice)\r
8966 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8967 /*\r
8968 REALTIME_PRIORITY_CLASS     0x00000100\r
8969 HIGH_PRIORITY_CLASS         0x00000080\r
8970 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8971 NORMAL_PRIORITY_CLASS       0x00000020\r
8972 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8973 IDLE_PRIORITY_CLASS         0x00000040\r
8974 */\r
8975         if (nice < -15) return 0x00000080;\r
8976         if (nice < 0)   return 0x00008000;\r
8977         if (nice == 0)  return 0x00000020;\r
8978         if (nice < 15)  return 0x00004000;\r
8979         return 0x00000040;\r
8980 }\r
8981 \r
8982 void RunCommand(char *cmdLine)\r
8983 {\r
8984   /* Now create the child process. */\r
8985   STARTUPINFO siStartInfo;\r
8986   PROCESS_INFORMATION piProcInfo;\r
8987 \r
8988   siStartInfo.cb = sizeof(STARTUPINFO);\r
8989   siStartInfo.lpReserved = NULL;\r
8990   siStartInfo.lpDesktop = NULL;\r
8991   siStartInfo.lpTitle = NULL;\r
8992   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8993   siStartInfo.cbReserved2 = 0;\r
8994   siStartInfo.lpReserved2 = NULL;\r
8995   siStartInfo.hStdInput = NULL;\r
8996   siStartInfo.hStdOutput = NULL;\r
8997   siStartInfo.hStdError = NULL;\r
8998 \r
8999   CreateProcess(NULL,\r
9000                 cmdLine,           /* command line */\r
9001                 NULL,      /* process security attributes */\r
9002                 NULL,      /* primary thread security attrs */\r
9003                 TRUE,      /* handles are inherited */\r
9004                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9005                 NULL,      /* use parent's environment */\r
9006                 NULL,\r
9007                 &siStartInfo, /* STARTUPINFO pointer */\r
9008                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9009 \r
9010   CloseHandle(piProcInfo.hThread);\r
9011 }\r
9012 \r
9013 /* Start a child process running the given program.\r
9014    The process's standard output can be read from "from", and its\r
9015    standard input can be written to "to".\r
9016    Exit with fatal error if anything goes wrong.\r
9017    Returns an opaque pointer that can be used to destroy the process\r
9018    later.\r
9019 */\r
9020 int\r
9021 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9022 {\r
9023 #define BUFSIZE 4096\r
9024 \r
9025   HANDLE hChildStdinRd, hChildStdinWr,\r
9026     hChildStdoutRd, hChildStdoutWr;\r
9027   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9028   SECURITY_ATTRIBUTES saAttr;\r
9029   BOOL fSuccess;\r
9030   PROCESS_INFORMATION piProcInfo;\r
9031   STARTUPINFO siStartInfo;\r
9032   ChildProc *cp;\r
9033   char buf[MSG_SIZ];\r
9034   DWORD err;\r
9035 \r
9036   if (appData.debugMode) {\r
9037     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9038   }\r
9039 \r
9040   *pr = NoProc;\r
9041 \r
9042   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9043   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9044   saAttr.bInheritHandle = TRUE;\r
9045   saAttr.lpSecurityDescriptor = NULL;\r
9046 \r
9047   /*\r
9048    * The steps for redirecting child's STDOUT:\r
9049    *     1. Create anonymous pipe to be STDOUT for child.\r
9050    *     2. Create a noninheritable duplicate of read handle,\r
9051    *         and close the inheritable read handle.\r
9052    */\r
9053 \r
9054   /* Create a pipe for the child's STDOUT. */\r
9055   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9056     return GetLastError();\r
9057   }\r
9058 \r
9059   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9060   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9061                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9062                              FALSE,     /* not inherited */\r
9063                              DUPLICATE_SAME_ACCESS);\r
9064   if (! fSuccess) {\r
9065     return GetLastError();\r
9066   }\r
9067   CloseHandle(hChildStdoutRd);\r
9068 \r
9069   /*\r
9070    * The steps for redirecting child's STDIN:\r
9071    *     1. Create anonymous pipe to be STDIN for child.\r
9072    *     2. Create a noninheritable duplicate of write handle,\r
9073    *         and close the inheritable write handle.\r
9074    */\r
9075 \r
9076   /* Create a pipe for the child's STDIN. */\r
9077   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9078     return GetLastError();\r
9079   }\r
9080 \r
9081   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9082   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9083                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9084                              FALSE,     /* not inherited */\r
9085                              DUPLICATE_SAME_ACCESS);\r
9086   if (! fSuccess) {\r
9087     return GetLastError();\r
9088   }\r
9089   CloseHandle(hChildStdinWr);\r
9090 \r
9091   /* Arrange to (1) look in dir for the child .exe file, and\r
9092    * (2) have dir be the child's working directory.  Interpret\r
9093    * dir relative to the directory WinBoard loaded from. */\r
9094   GetCurrentDirectory(MSG_SIZ, buf);\r
9095   SetCurrentDirectory(installDir);\r
9096   SetCurrentDirectory(dir);\r
9097 \r
9098   /* Now create the child process. */\r
9099 \r
9100   siStartInfo.cb = sizeof(STARTUPINFO);\r
9101   siStartInfo.lpReserved = NULL;\r
9102   siStartInfo.lpDesktop = NULL;\r
9103   siStartInfo.lpTitle = NULL;\r
9104   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9105   siStartInfo.cbReserved2 = 0;\r
9106   siStartInfo.lpReserved2 = NULL;\r
9107   siStartInfo.hStdInput = hChildStdinRd;\r
9108   siStartInfo.hStdOutput = hChildStdoutWr;\r
9109   siStartInfo.hStdError = hChildStdoutWr;\r
9110 \r
9111   fSuccess = CreateProcess(NULL,\r
9112                            cmdLine,        /* command line */\r
9113                            NULL,           /* process security attributes */\r
9114                            NULL,           /* primary thread security attrs */\r
9115                            TRUE,           /* handles are inherited */\r
9116                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9117                            NULL,           /* use parent's environment */\r
9118                            NULL,\r
9119                            &siStartInfo, /* STARTUPINFO pointer */\r
9120                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9121 \r
9122   err = GetLastError();\r
9123   SetCurrentDirectory(buf); /* return to prev directory */\r
9124   if (! fSuccess) {\r
9125     return err;\r
9126   }\r
9127 \r
9128   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9129     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9130     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9131   }\r
9132 \r
9133   /* Close the handles we don't need in the parent */\r
9134   CloseHandle(piProcInfo.hThread);\r
9135   CloseHandle(hChildStdinRd);\r
9136   CloseHandle(hChildStdoutWr);\r
9137 \r
9138   /* Prepare return value */\r
9139   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9140   cp->kind = CPReal;\r
9141   cp->hProcess = piProcInfo.hProcess;\r
9142   cp->pid = piProcInfo.dwProcessId;\r
9143   cp->hFrom = hChildStdoutRdDup;\r
9144   cp->hTo = hChildStdinWrDup;\r
9145 \r
9146   *pr = (void *) cp;\r
9147 \r
9148   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9149      2000 where engines sometimes don't see the initial command(s)\r
9150      from WinBoard and hang.  I don't understand how that can happen,\r
9151      but the Sleep is harmless, so I've put it in.  Others have also\r
9152      reported what may be the same problem, so hopefully this will fix\r
9153      it for them too.  */\r
9154   Sleep(500);\r
9155 \r
9156   return NO_ERROR;\r
9157 }\r
9158 \r
9159 \r
9160 void\r
9161 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9162 {\r
9163   ChildProc *cp; int result;\r
9164 \r
9165   cp = (ChildProc *) pr;\r
9166   if (cp == NULL) return;\r
9167 \r
9168   switch (cp->kind) {\r
9169   case CPReal:\r
9170     /* TerminateProcess is considered harmful, so... */\r
9171     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9172     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9173     /* The following doesn't work because the chess program\r
9174        doesn't "have the same console" as WinBoard.  Maybe\r
9175        we could arrange for this even though neither WinBoard\r
9176        nor the chess program uses a console for stdio? */\r
9177     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9178 \r
9179     /* [AS] Special termination modes for misbehaving programs... */\r
9180     if( signal == 9 ) { \r
9181         result = TerminateProcess( cp->hProcess, 0 );\r
9182 \r
9183         if ( appData.debugMode) {\r
9184             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9185         }\r
9186     }\r
9187     else if( signal == 10 ) {\r
9188         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9189 \r
9190         if( dw != WAIT_OBJECT_0 ) {\r
9191             result = TerminateProcess( cp->hProcess, 0 );\r
9192 \r
9193             if ( appData.debugMode) {\r
9194                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9195             }\r
9196 \r
9197         }\r
9198     }\r
9199 \r
9200     CloseHandle(cp->hProcess);\r
9201     break;\r
9202 \r
9203   case CPComm:\r
9204     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9205     break;\r
9206 \r
9207   case CPSock:\r
9208     closesocket(cp->sock);\r
9209     WSACleanup();\r
9210     break;\r
9211 \r
9212   case CPRcmd:\r
9213     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9214     closesocket(cp->sock);\r
9215     closesocket(cp->sock2);\r
9216     WSACleanup();\r
9217     break;\r
9218   }\r
9219   free(cp);\r
9220 }\r
9221 \r
9222 void\r
9223 InterruptChildProcess(ProcRef pr)\r
9224 {\r
9225   ChildProc *cp;\r
9226 \r
9227   cp = (ChildProc *) pr;\r
9228   if (cp == NULL) return;\r
9229   switch (cp->kind) {\r
9230   case CPReal:\r
9231     /* The following doesn't work because the chess program\r
9232        doesn't "have the same console" as WinBoard.  Maybe\r
9233        we could arrange for this even though neither WinBoard\r
9234        nor the chess program uses a console for stdio */\r
9235     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9236     break;\r
9237 \r
9238   case CPComm:\r
9239   case CPSock:\r
9240     /* Can't interrupt */\r
9241     break;\r
9242 \r
9243   case CPRcmd:\r
9244     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9245     break;\r
9246   }\r
9247 }\r
9248 \r
9249 \r
9250 int\r
9251 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9252 {\r
9253   char cmdLine[MSG_SIZ];\r
9254 \r
9255   if (port[0] == NULLCHAR) {\r
9256     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9257   } else {\r
9258     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9259   }\r
9260   return StartChildProcess(cmdLine, "", pr);\r
9261 }\r
9262 \r
9263 \r
9264 /* Code to open TCP sockets */\r
9265 \r
9266 int\r
9267 OpenTCP(char *host, char *port, ProcRef *pr)\r
9268 {\r
9269   ChildProc *cp;\r
9270   int err;\r
9271   SOCKET s;\r
9272 \r
9273   struct sockaddr_in sa, mysa;\r
9274   struct hostent FAR *hp;\r
9275   unsigned short uport;\r
9276   WORD wVersionRequested;\r
9277   WSADATA wsaData;\r
9278 \r
9279   /* Initialize socket DLL */\r
9280   wVersionRequested = MAKEWORD(1, 1);\r
9281   err = WSAStartup(wVersionRequested, &wsaData);\r
9282   if (err != 0) return err;\r
9283 \r
9284   /* Make socket */\r
9285   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9286     err = WSAGetLastError();\r
9287     WSACleanup();\r
9288     return err;\r
9289   }\r
9290 \r
9291   /* Bind local address using (mostly) don't-care values.\r
9292    */\r
9293   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9294   mysa.sin_family = AF_INET;\r
9295   mysa.sin_addr.s_addr = INADDR_ANY;\r
9296   uport = (unsigned short) 0;\r
9297   mysa.sin_port = htons(uport);\r
9298   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9299       == SOCKET_ERROR) {\r
9300     err = WSAGetLastError();\r
9301     WSACleanup();\r
9302     return err;\r
9303   }\r
9304 \r
9305   /* Resolve remote host name */\r
9306   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9307   if (!(hp = gethostbyname(host))) {\r
9308     unsigned int b0, b1, b2, b3;\r
9309 \r
9310     err = WSAGetLastError();\r
9311 \r
9312     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9313       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9314       hp->h_addrtype = AF_INET;\r
9315       hp->h_length = 4;\r
9316       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9317       hp->h_addr_list[0] = (char *) malloc(4);\r
9318       hp->h_addr_list[0][0] = (char) b0;\r
9319       hp->h_addr_list[0][1] = (char) b1;\r
9320       hp->h_addr_list[0][2] = (char) b2;\r
9321       hp->h_addr_list[0][3] = (char) b3;\r
9322     } else {\r
9323       WSACleanup();\r
9324       return err;\r
9325     }\r
9326   }\r
9327   sa.sin_family = hp->h_addrtype;\r
9328   uport = (unsigned short) atoi(port);\r
9329   sa.sin_port = htons(uport);\r
9330   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9331 \r
9332   /* Make connection */\r
9333   if (connect(s, (struct sockaddr *) &sa,\r
9334               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9335     err = WSAGetLastError();\r
9336     WSACleanup();\r
9337     return err;\r
9338   }\r
9339 \r
9340   /* Prepare return value */\r
9341   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9342   cp->kind = CPSock;\r
9343   cp->sock = s;\r
9344   *pr = (ProcRef *) cp;\r
9345 \r
9346   return NO_ERROR;\r
9347 }\r
9348 \r
9349 int\r
9350 OpenCommPort(char *name, ProcRef *pr)\r
9351 {\r
9352   HANDLE h;\r
9353   COMMTIMEOUTS ct;\r
9354   ChildProc *cp;\r
9355   char fullname[MSG_SIZ];\r
9356 \r
9357   if (*name != '\\')\r
9358     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9359   else\r
9360     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9361 \r
9362   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9363                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9364   if (h == (HANDLE) -1) {\r
9365     return GetLastError();\r
9366   }\r
9367   hCommPort = h;\r
9368 \r
9369   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9370 \r
9371   /* Accumulate characters until a 100ms pause, then parse */\r
9372   ct.ReadIntervalTimeout = 100;\r
9373   ct.ReadTotalTimeoutMultiplier = 0;\r
9374   ct.ReadTotalTimeoutConstant = 0;\r
9375   ct.WriteTotalTimeoutMultiplier = 0;\r
9376   ct.WriteTotalTimeoutConstant = 0;\r
9377   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9378 \r
9379   /* Prepare return value */\r
9380   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9381   cp->kind = CPComm;\r
9382   cp->hFrom = h;\r
9383   cp->hTo = h;\r
9384   *pr = (ProcRef *) cp;\r
9385 \r
9386   return NO_ERROR;\r
9387 }\r
9388 \r
9389 int\r
9390 OpenLoopback(ProcRef *pr)\r
9391 {\r
9392   DisplayFatalError(_("Not implemented"), 0, 1);\r
9393   return NO_ERROR;\r
9394 }\r
9395 \r
9396 \r
9397 int\r
9398 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9399 {\r
9400   ChildProc *cp;\r
9401   int err;\r
9402   SOCKET s, s2, s3;\r
9403   struct sockaddr_in sa, mysa;\r
9404   struct hostent FAR *hp;\r
9405   unsigned short uport;\r
9406   WORD wVersionRequested;\r
9407   WSADATA wsaData;\r
9408   int fromPort;\r
9409   char stderrPortStr[MSG_SIZ];\r
9410 \r
9411   /* Initialize socket DLL */\r
9412   wVersionRequested = MAKEWORD(1, 1);\r
9413   err = WSAStartup(wVersionRequested, &wsaData);\r
9414   if (err != 0) return err;\r
9415 \r
9416   /* Resolve remote host name */\r
9417   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9418   if (!(hp = gethostbyname(host))) {\r
9419     unsigned int b0, b1, b2, b3;\r
9420 \r
9421     err = WSAGetLastError();\r
9422 \r
9423     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9424       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9425       hp->h_addrtype = AF_INET;\r
9426       hp->h_length = 4;\r
9427       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9428       hp->h_addr_list[0] = (char *) malloc(4);\r
9429       hp->h_addr_list[0][0] = (char) b0;\r
9430       hp->h_addr_list[0][1] = (char) b1;\r
9431       hp->h_addr_list[0][2] = (char) b2;\r
9432       hp->h_addr_list[0][3] = (char) b3;\r
9433     } else {\r
9434       WSACleanup();\r
9435       return err;\r
9436     }\r
9437   }\r
9438   sa.sin_family = hp->h_addrtype;\r
9439   uport = (unsigned short) 514;\r
9440   sa.sin_port = htons(uport);\r
9441   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9442 \r
9443   /* Bind local socket to unused "privileged" port address\r
9444    */\r
9445   s = INVALID_SOCKET;\r
9446   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9447   mysa.sin_family = AF_INET;\r
9448   mysa.sin_addr.s_addr = INADDR_ANY;\r
9449   for (fromPort = 1023;; fromPort--) {\r
9450     if (fromPort < 0) {\r
9451       WSACleanup();\r
9452       return WSAEADDRINUSE;\r
9453     }\r
9454     if (s == INVALID_SOCKET) {\r
9455       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9456         err = WSAGetLastError();\r
9457         WSACleanup();\r
9458         return err;\r
9459       }\r
9460     }\r
9461     uport = (unsigned short) fromPort;\r
9462     mysa.sin_port = htons(uport);\r
9463     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9464         == SOCKET_ERROR) {\r
9465       err = WSAGetLastError();\r
9466       if (err == WSAEADDRINUSE) continue;\r
9467       WSACleanup();\r
9468       return err;\r
9469     }\r
9470     if (connect(s, (struct sockaddr *) &sa,\r
9471       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9472       err = WSAGetLastError();\r
9473       if (err == WSAEADDRINUSE) {\r
9474         closesocket(s);\r
9475         s = -1;\r
9476         continue;\r
9477       }\r
9478       WSACleanup();\r
9479       return err;\r
9480     }\r
9481     break;\r
9482   }\r
9483 \r
9484   /* Bind stderr local socket to unused "privileged" port address\r
9485    */\r
9486   s2 = INVALID_SOCKET;\r
9487   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9488   mysa.sin_family = AF_INET;\r
9489   mysa.sin_addr.s_addr = INADDR_ANY;\r
9490   for (fromPort = 1023;; fromPort--) {\r
9491     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9492     if (fromPort < 0) {\r
9493       (void) closesocket(s);\r
9494       WSACleanup();\r
9495       return WSAEADDRINUSE;\r
9496     }\r
9497     if (s2 == INVALID_SOCKET) {\r
9498       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9499         err = WSAGetLastError();\r
9500         closesocket(s);\r
9501         WSACleanup();\r
9502         return err;\r
9503       }\r
9504     }\r
9505     uport = (unsigned short) fromPort;\r
9506     mysa.sin_port = htons(uport);\r
9507     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9508         == SOCKET_ERROR) {\r
9509       err = WSAGetLastError();\r
9510       if (err == WSAEADDRINUSE) continue;\r
9511       (void) closesocket(s);\r
9512       WSACleanup();\r
9513       return err;\r
9514     }\r
9515     if (listen(s2, 1) == SOCKET_ERROR) {\r
9516       err = WSAGetLastError();\r
9517       if (err == WSAEADDRINUSE) {\r
9518         closesocket(s2);\r
9519         s2 = INVALID_SOCKET;\r
9520         continue;\r
9521       }\r
9522       (void) closesocket(s);\r
9523       (void) closesocket(s2);\r
9524       WSACleanup();\r
9525       return err;\r
9526     }\r
9527     break;\r
9528   }\r
9529   prevStderrPort = fromPort; // remember port used\r
9530   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9531 \r
9532   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9533     err = WSAGetLastError();\r
9534     (void) closesocket(s);\r
9535     (void) closesocket(s2);\r
9536     WSACleanup();\r
9537     return err;\r
9538   }\r
9539 \r
9540   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9541     err = WSAGetLastError();\r
9542     (void) closesocket(s);\r
9543     (void) closesocket(s2);\r
9544     WSACleanup();\r
9545     return err;\r
9546   }\r
9547   if (*user == NULLCHAR) user = UserName();\r
9548   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9549     err = WSAGetLastError();\r
9550     (void) closesocket(s);\r
9551     (void) closesocket(s2);\r
9552     WSACleanup();\r
9553     return err;\r
9554   }\r
9555   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9556     err = WSAGetLastError();\r
9557     (void) closesocket(s);\r
9558     (void) closesocket(s2);\r
9559     WSACleanup();\r
9560     return err;\r
9561   }\r
9562 \r
9563   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9564     err = WSAGetLastError();\r
9565     (void) closesocket(s);\r
9566     (void) closesocket(s2);\r
9567     WSACleanup();\r
9568     return err;\r
9569   }\r
9570   (void) closesocket(s2);  /* Stop listening */\r
9571 \r
9572   /* Prepare return value */\r
9573   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9574   cp->kind = CPRcmd;\r
9575   cp->sock = s;\r
9576   cp->sock2 = s3;\r
9577   *pr = (ProcRef *) cp;\r
9578 \r
9579   return NO_ERROR;\r
9580 }\r
9581 \r
9582 \r
9583 InputSourceRef\r
9584 AddInputSource(ProcRef pr, int lineByLine,\r
9585                InputCallback func, VOIDSTAR closure)\r
9586 {\r
9587   InputSource *is, *is2 = NULL;\r
9588   ChildProc *cp = (ChildProc *) pr;\r
9589 \r
9590   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9591   is->lineByLine = lineByLine;\r
9592   is->func = func;\r
9593   is->closure = closure;\r
9594   is->second = NULL;\r
9595   is->next = is->buf;\r
9596   if (pr == NoProc) {\r
9597     is->kind = CPReal;\r
9598     consoleInputSource = is;\r
9599   } else {\r
9600     is->kind = cp->kind;\r
9601     /* \r
9602         [AS] Try to avoid a race condition if the thread is given control too early:\r
9603         we create all threads suspended so that the is->hThread variable can be\r
9604         safely assigned, then let the threads start with ResumeThread.\r
9605     */\r
9606     switch (cp->kind) {\r
9607     case CPReal:\r
9608       is->hFile = cp->hFrom;\r
9609       cp->hFrom = NULL; /* now owned by InputThread */\r
9610       is->hThread =\r
9611         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9612                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9613       break;\r
9614 \r
9615     case CPComm:\r
9616       is->hFile = cp->hFrom;\r
9617       cp->hFrom = NULL; /* now owned by InputThread */\r
9618       is->hThread =\r
9619         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9620                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9621       break;\r
9622 \r
9623     case CPSock:\r
9624       is->sock = cp->sock;\r
9625       is->hThread =\r
9626         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9627                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9628       break;\r
9629 \r
9630     case CPRcmd:\r
9631       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9632       *is2 = *is;\r
9633       is->sock = cp->sock;\r
9634       is->second = is2;\r
9635       is2->sock = cp->sock2;\r
9636       is2->second = is2;\r
9637       is->hThread =\r
9638         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9639                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9640       is2->hThread =\r
9641         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9642                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9643       break;\r
9644     }\r
9645 \r
9646     if( is->hThread != NULL ) {\r
9647         ResumeThread( is->hThread );\r
9648     }\r
9649 \r
9650     if( is2 != NULL && is2->hThread != NULL ) {\r
9651         ResumeThread( is2->hThread );\r
9652     }\r
9653   }\r
9654 \r
9655   return (InputSourceRef) is;\r
9656 }\r
9657 \r
9658 void\r
9659 RemoveInputSource(InputSourceRef isr)\r
9660 {\r
9661   InputSource *is;\r
9662 \r
9663   is = (InputSource *) isr;\r
9664   is->hThread = NULL;  /* tell thread to stop */\r
9665   CloseHandle(is->hThread);\r
9666   if (is->second != NULL) {\r
9667     is->second->hThread = NULL;\r
9668     CloseHandle(is->second->hThread);\r
9669   }\r
9670 }\r
9671 \r
9672 int no_wrap(char *message, int count)\r
9673 {\r
9674     ConsoleOutput(message, count, FALSE);\r
9675     return count;\r
9676 }\r
9677 \r
9678 int\r
9679 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9680 {\r
9681   DWORD dOutCount;\r
9682   int outCount = SOCKET_ERROR;\r
9683   ChildProc *cp = (ChildProc *) pr;\r
9684   static OVERLAPPED ovl;\r
9685   static int line = 0;\r
9686 \r
9687   if (pr == NoProc)\r
9688   {\r
9689     if (appData.noJoin || !appData.useInternalWrap)\r
9690       return no_wrap(message, count);\r
9691     else\r
9692     {\r
9693       int width = get_term_width();\r
9694       int len = wrap(NULL, message, count, width, &line);\r
9695       char *msg = malloc(len);\r
9696       int dbgchk;\r
9697 \r
9698       if (!msg)\r
9699         return no_wrap(message, count);\r
9700       else\r
9701       {\r
9702         dbgchk = wrap(msg, message, count, width, &line);\r
9703         if (dbgchk != len && appData.debugMode)\r
9704             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9705         ConsoleOutput(msg, len, FALSE);\r
9706         free(msg);\r
9707         return len;\r
9708       }\r
9709     }\r
9710   }\r
9711 \r
9712   if (ovl.hEvent == NULL) {\r
9713     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9714   }\r
9715   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9716 \r
9717   switch (cp->kind) {\r
9718   case CPSock:\r
9719   case CPRcmd:\r
9720     outCount = send(cp->sock, message, count, 0);\r
9721     if (outCount == SOCKET_ERROR) {\r
9722       *outError = WSAGetLastError();\r
9723     } else {\r
9724       *outError = NO_ERROR;\r
9725     }\r
9726     break;\r
9727 \r
9728   case CPReal:\r
9729     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9730                   &dOutCount, NULL)) {\r
9731       *outError = NO_ERROR;\r
9732       outCount = (int) dOutCount;\r
9733     } else {\r
9734       *outError = GetLastError();\r
9735     }\r
9736     break;\r
9737 \r
9738   case CPComm:\r
9739     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9740                             &dOutCount, &ovl);\r
9741     if (*outError == NO_ERROR) {\r
9742       outCount = (int) dOutCount;\r
9743     }\r
9744     break;\r
9745   }\r
9746   return outCount;\r
9747 }\r
9748 \r
9749 void\r
9750 DoSleep(int n)\r
9751 {\r
9752     if(n != 0) Sleep(n);\r
9753 }\r
9754 \r
9755 int\r
9756 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9757                        long msdelay)\r
9758 {\r
9759   /* Ignore delay, not implemented for WinBoard */\r
9760   return OutputToProcess(pr, message, count, outError);\r
9761 }\r
9762 \r
9763 \r
9764 void\r
9765 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9766                         char *buf, int count, int error)\r
9767 {\r
9768   DisplayFatalError(_("Not implemented"), 0, 1);\r
9769 }\r
9770 \r
9771 /* see wgamelist.c for Game List functions */\r
9772 /* see wedittags.c for Edit Tags functions */\r
9773 \r
9774 \r
9775 int\r
9776 ICSInitScript()\r
9777 {\r
9778   FILE *f;\r
9779   char buf[MSG_SIZ];\r
9780   char *dummy;\r
9781 \r
9782   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9783     f = fopen(buf, "r");\r
9784     if (f != NULL) {\r
9785       ProcessICSInitScript(f);\r
9786       fclose(f);\r
9787       return TRUE;\r
9788     }\r
9789   }\r
9790   return FALSE;\r
9791 }\r
9792 \r
9793 \r
9794 VOID\r
9795 StartAnalysisClock()\r
9796 {\r
9797   if (analysisTimerEvent) return;\r
9798   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9799                                         (UINT) 2000, NULL);\r
9800 }\r
9801 \r
9802 VOID\r
9803 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9804 {\r
9805   highlightInfo.sq[0].x = fromX;\r
9806   highlightInfo.sq[0].y = fromY;\r
9807   highlightInfo.sq[1].x = toX;\r
9808   highlightInfo.sq[1].y = toY;\r
9809 }\r
9810 \r
9811 VOID\r
9812 ClearHighlights()\r
9813 {\r
9814   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9815     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9816 }\r
9817 \r
9818 VOID\r
9819 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9820 {\r
9821   premoveHighlightInfo.sq[0].x = fromX;\r
9822   premoveHighlightInfo.sq[0].y = fromY;\r
9823   premoveHighlightInfo.sq[1].x = toX;\r
9824   premoveHighlightInfo.sq[1].y = toY;\r
9825 }\r
9826 \r
9827 VOID\r
9828 ClearPremoveHighlights()\r
9829 {\r
9830   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9831     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9832 }\r
9833 \r
9834 VOID\r
9835 ShutDownFrontEnd()\r
9836 {\r
9837   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9838   DeleteClipboardTempFiles();\r
9839 }\r
9840 \r
9841 void\r
9842 BoardToTop()\r
9843 {\r
9844     if (IsIconic(hwndMain))\r
9845       ShowWindow(hwndMain, SW_RESTORE);\r
9846 \r
9847     SetActiveWindow(hwndMain);\r
9848 }\r
9849 \r
9850 /*\r
9851  * Prototypes for animation support routines\r
9852  */\r
9853 static void ScreenSquare(int column, int row, POINT * pt);\r
9854 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9855      POINT frames[], int * nFrames);\r
9856 \r
9857 \r
9858 #define kFactor 4\r
9859 \r
9860 void\r
9861 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9862 {       // [HGM] atomic: animate blast wave\r
9863         int i;\r
9864 \r
9865         explodeInfo.fromX = fromX;\r
9866         explodeInfo.fromY = fromY;\r
9867         explodeInfo.toX = toX;\r
9868         explodeInfo.toY = toY;\r
9869         for(i=1; i<4*kFactor; i++) {\r
9870             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9871             DrawPosition(FALSE, board);\r
9872             Sleep(appData.animSpeed);\r
9873         }\r
9874         explodeInfo.radius = 0;\r
9875         DrawPosition(TRUE, board);\r
9876 }\r
9877 \r
9878 void\r
9879 AnimateMove(board, fromX, fromY, toX, toY)\r
9880      Board board;\r
9881      int fromX;\r
9882      int fromY;\r
9883      int toX;\r
9884      int toY;\r
9885 {\r
9886   ChessSquare piece;\r
9887   POINT start, finish, mid;\r
9888   POINT frames[kFactor * 2 + 1];\r
9889   int nFrames, n;\r
9890 \r
9891   if (!appData.animate) return;\r
9892   if (doingSizing) return;\r
9893   if (fromY < 0 || fromX < 0) return;\r
9894   piece = board[fromY][fromX];\r
9895   if (piece >= EmptySquare) return;\r
9896 \r
9897   ScreenSquare(fromX, fromY, &start);\r
9898   ScreenSquare(toX, toY, &finish);\r
9899 \r
9900   /* All moves except knight jumps move in straight line */\r
9901   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9902     mid.x = start.x + (finish.x - start.x) / 2;\r
9903     mid.y = start.y + (finish.y - start.y) / 2;\r
9904   } else {\r
9905     /* Knight: make straight movement then diagonal */\r
9906     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9907        mid.x = start.x + (finish.x - start.x) / 2;\r
9908        mid.y = start.y;\r
9909      } else {\r
9910        mid.x = start.x;\r
9911        mid.y = start.y + (finish.y - start.y) / 2;\r
9912      }\r
9913   }\r
9914   \r
9915   /* Don't use as many frames for very short moves */\r
9916   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9917     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9918   else\r
9919     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9920 \r
9921   animInfo.from.x = fromX;\r
9922   animInfo.from.y = fromY;\r
9923   animInfo.to.x = toX;\r
9924   animInfo.to.y = toY;\r
9925   animInfo.lastpos = start;\r
9926   animInfo.piece = piece;\r
9927   for (n = 0; n < nFrames; n++) {\r
9928     animInfo.pos = frames[n];\r
9929     DrawPosition(FALSE, NULL);\r
9930     animInfo.lastpos = animInfo.pos;\r
9931     Sleep(appData.animSpeed);\r
9932   }\r
9933   animInfo.pos = finish;\r
9934   DrawPosition(FALSE, NULL);\r
9935   animInfo.piece = EmptySquare;\r
9936   Explode(board, fromX, fromY, toX, toY);\r
9937 }\r
9938 \r
9939 /*      Convert board position to corner of screen rect and color       */\r
9940 \r
9941 static void\r
9942 ScreenSquare(column, row, pt)\r
9943      int column; int row; POINT * pt;\r
9944 {\r
9945   if (flipView) {\r
9946     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9947     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9948   } else {\r
9949     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9950     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9951   }\r
9952 }\r
9953 \r
9954 /*      Generate a series of frame coords from start->mid->finish.\r
9955         The movement rate doubles until the half way point is\r
9956         reached, then halves back down to the final destination,\r
9957         which gives a nice slow in/out effect. The algorithmn\r
9958         may seem to generate too many intermediates for short\r
9959         moves, but remember that the purpose is to attract the\r
9960         viewers attention to the piece about to be moved and\r
9961         then to where it ends up. Too few frames would be less\r
9962         noticeable.                                             */\r
9963 \r
9964 static void\r
9965 Tween(start, mid, finish, factor, frames, nFrames)\r
9966      POINT * start; POINT * mid;\r
9967      POINT * finish; int factor;\r
9968      POINT frames[]; int * nFrames;\r
9969 {\r
9970   int n, fraction = 1, count = 0;\r
9971 \r
9972   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9973   for (n = 0; n < factor; n++)\r
9974     fraction *= 2;\r
9975   for (n = 0; n < factor; n++) {\r
9976     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9977     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9978     count ++;\r
9979     fraction = fraction / 2;\r
9980   }\r
9981   \r
9982   /* Midpoint */\r
9983   frames[count] = *mid;\r
9984   count ++;\r
9985   \r
9986   /* Slow out, stepping 1/2, then 1/4, ... */\r
9987   fraction = 2;\r
9988   for (n = 0; n < factor; n++) {\r
9989     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9990     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9991     count ++;\r
9992     fraction = fraction * 2;\r
9993   }\r
9994   *nFrames = count;\r
9995 }\r
9996 \r
9997 void\r
9998 SettingsPopUp(ChessProgramState *cps)\r
9999 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10000       EngineOptionsPopup(savedHwnd, cps);\r
10001 }\r
10002 \r
10003 int flock(int fid, int code)\r
10004 {\r
10005     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10006     OVERLAPPED ov;\r
10007     ov.hEvent = NULL;\r
10008     ov.Offset = 0;\r
10009     ov.OffsetHigh = 0;\r
10010     switch(code) {\r
10011       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10012       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10013       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10014       default: return -1;\r
10015     }\r
10016     return 0;\r
10017 }\r
10018 \r
10019 char *\r
10020 Col2Text (int n)\r
10021 {\r
10022     static int i=0;\r
10023     static char col[8][20];\r
10024     COLORREF color = *(COLORREF *) colorVariable[n];\r
10025     i = i+1 & 7;\r
10026     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10027     return col[i];\r
10028 }\r
10029 \r
10030 void\r
10031 ActivateTheme (int new)\r
10032 {   // Redo initialization of features depending on options that can occur in themes\r
10033    InitTextures();\r
10034    if(new) InitDrawingColors();\r
10035    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10036    InitDrawingSizes(-2, 0);\r
10037    InvalidateRect(hwndMain, NULL, TRUE);\r
10038 }\r