Put 'Load Next' button back in WB Game List
[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 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 int ics_type;\r
104 \r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
106 VOID NewVariantPopup(HWND hwnd);\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
108                    /*char*/int promoChar));\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127   ChessSquare piece;\r
128 } DragInfo;\r
129 \r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
131 \r
132 typedef struct {\r
133   POINT sq[2];    /* board coordinates of from, to squares */\r
134 } HighlightInfo;\r
135 \r
136 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
140 \r
141 typedef struct { // [HGM] atomic\r
142   int fromX, fromY, toX, toY, radius;\r
143 } ExplodeInfo;\r
144 \r
145 static ExplodeInfo explodeInfo;\r
146 \r
147 /* Window class names */\r
148 char szAppName[] = "WinBoard";\r
149 char szConsoleName[] = "WBConsole";\r
150 \r
151 /* Title bar text */\r
152 char szTitle[] = "WinBoard";\r
153 char szConsoleTitle[] = "I C S Interaction";\r
154 \r
155 char *programName;\r
156 char *settingsFileName;\r
157 Boolean saveSettingsOnExit;\r
158 char installDir[MSG_SIZ];\r
159 int errorExitStatus;\r
160 \r
161 BoardSize boardSize;\r
162 Boolean chessProgram;\r
163 //static int boardX, boardY;\r
164 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
165 int squareSize, lineGap, minorSize;\r
166 static int winW, winH;\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
168 static int logoHeight = 0;\r
169 static char messageText[MESSAGE_TEXT_MAX];\r
170 static int clockTimerEvent = 0;\r
171 static int loadGameTimerEvent = 0;\r
172 static int analysisTimerEvent = 0;\r
173 static DelayedEventCallback delayedTimerCallback;\r
174 static int delayedTimerEvent = 0;\r
175 static int buttonCount = 2;\r
176 char *icsTextMenuString;\r
177 char *icsNames;\r
178 char *firstChessProgramNames;\r
179 char *secondChessProgramNames;\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 Boolean alwaysOnTop = FALSE;\r
185 RECT boardRect;\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
187   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
188 HPALETTE hPal;\r
189 ColorClass currentColorClass;\r
190 \r
191 static HWND savedHwnd;\r
192 HWND hCommPort = NULL;    /* currently open comm port */\r
193 static HWND hwndPause;    /* pause button */\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,\r
196   blackSquareBrush, /* [HGM] for band between board and holdings */\r
197   explodeBrush,     /* [HGM] atomic */\r
198   markerBrush,      /* [HGM] markers */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 static HBITMAP liteBackTexture = NULL;\r
214 static HBITMAP darkBackTexture = NULL;\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int backTextureSquareSize = 0;\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
219 \r
220 #if __GNUC__ && !defined(_winmajor)\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
222 #else\r
223 #if defined(_winmajor)\r
224 #define oldDialog (_winmajor < 4)\r
225 #else\r
226 #define oldDialog 0\r
227 #endif\r
228 #endif\r
229 \r
230 #define INTERNATIONAL\r
231 \r
232 #ifdef INTERNATIONAL\r
233 #  define _(s) T_(s)\r
234 #  define N_(s) s\r
235 #else\r
236 #  define _(s) s\r
237 #  define N_(s) s\r
238 #  define T_(s) s\r
239 #  define Translate(x, y)\r
240 #  define LoadLanguageFile(s)\r
241 #endif\r
242 \r
243 #ifdef INTERNATIONAL\r
244 \r
245 Boolean barbaric; // flag indicating if translation is needed\r
246 \r
247 // list of item numbers used in each dialog (used to alter language at run time)\r
248 \r
249 #define ABOUTBOX -1  /* not sure why these are needed */\r
250 #define ABOUTBOX2 -1\r
251 \r
252 int dialogItems[][41    ] = {\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
255   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
257   OPT_elo1, OPT_elo2, OPT_date, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, IDOK, IDCANCEL }, \r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
259   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
262   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
265   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
266 { ABOUTBOX2, IDC_ChessBoard }, \r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
268   OPT_GameListClose, IDC_GameListDoFilter }, \r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
270 { DLG_Error, IDOK }, \r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
272   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
275   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
276   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
277 { DLG_IndexNumber, IDC_Index }, \r
278 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
279 { DLG_TypeInName, IDOK, IDCANCEL }, \r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
281   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
283   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
284   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
285   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
286   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
287   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
288   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
290   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
291   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
292   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
293   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
294   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
295   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
296   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
297   GPB_General, GPB_Alarm }, \r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
299   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
300   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
301   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
302   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
303   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
304   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
305   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont }, \r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
307   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
308   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
309   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
310   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
311   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
312   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
313   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
314   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
316   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
317   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
318   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
319   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
322   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
323 { DLG_MoveHistory }, \r
324 { DLG_EvalGraph }, \r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
328   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
329   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
330   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
332   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
333   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
334 { 0 }\r
335 };\r
336 \r
337 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
338 static int lastChecked;\r
339 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
340 extern int tinyLayout;\r
341 extern char * menuBarText[][10];\r
342 \r
343 void\r
344 LoadLanguageFile(char *name)\r
345 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
346     FILE *f;\r
347     int i=0, j=0, n=0, k;\r
348     char buf[MSG_SIZ];\r
349 \r
350     if(!name || name[0] == NULLCHAR) return;\r
351       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
352     appData.language = oldLanguage;\r
353     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
354     if((f = fopen(buf, "r")) == NULL) return;\r
355     while((k = fgetc(f)) != EOF) {\r
356         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
357         languageBuf[i] = k;\r
358         if(k == '\n') {\r
359             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
360                 char *p;\r
361                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
362                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
363                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
364                         english[j] = languageBuf + n + 1; *p = 0;\r
365                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
366 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
367                     }\r
368                 }\r
369             }\r
370             n = i + 1;\r
371         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
372             switch(k) {\r
373               case 'n': k = '\n'; break;\r
374               case 'r': k = '\r'; break;\r
375               case 't': k = '\t'; break;\r
376             }\r
377             languageBuf[--i] = k;\r
378         }\r
379         i++;\r
380     }\r
381     fclose(f);\r
382     barbaric = (j != 0);\r
383     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
384 }\r
385 \r
386 char *\r
387 T_(char *s)\r
388 {   // return the translation of the given string\r
389     // efficiency can be improved a lot...\r
390     int i=0;\r
391 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
392     if(!barbaric) return s;\r
393     if(!s) return ""; // sanity\r
394     while(english[i]) {\r
395         if(!strcmp(s, english[i])) return foreign[i];\r
396         i++;\r
397     }\r
398     return s;\r
399 }\r
400 \r
401 void\r
402 Translate(HWND hDlg, int dialogID)\r
403 {   // translate all text items in the given dialog\r
404     int i=0, j, k;\r
405     char buf[MSG_SIZ], *s;\r
406     if(!barbaric) return;\r
407     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
408     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
409     GetWindowText( hDlg, buf, MSG_SIZ );\r
410     s = T_(buf);\r
411     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
412     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
413         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
414         if(strlen(buf) == 0) continue;\r
415         s = T_(buf);\r
416         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
417     }\r
418 }\r
419 \r
420 HMENU\r
421 TranslateOneMenu(int i, HMENU subMenu)\r
422 {\r
423     int j;\r
424     static MENUITEMINFO info;\r
425 \r
426     info.cbSize = sizeof(MENUITEMINFO);\r
427     info.fMask = MIIM_STATE | MIIM_TYPE;\r
428           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
429             char buf[MSG_SIZ];\r
430             info.dwTypeData = buf;\r
431             info.cch = sizeof(buf);\r
432             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
433             if(i < 10) {
434                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
435                 else menuText[i][j] = strdup(buf); // remember original on first change\r
436             }\r
437             if(buf[0] == NULLCHAR) continue;\r
438             info.dwTypeData = T_(buf);\r
439             info.cch = strlen(buf)+1;\r
440             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
441           }\r
442     return subMenu;\r
443 }\r
444 \r
445 void\r
446 TranslateMenus(int addLanguage)\r
447 {\r
448     int i;\r
449     WIN32_FIND_DATA fileData;\r
450     HANDLE hFind;\r
451 #define IDM_English 1970\r
452     if(1) {\r
453         HMENU mainMenu = GetMenu(hwndMain);\r
454         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
455           HMENU subMenu = GetSubMenu(mainMenu, i);\r
456           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
457                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
458           TranslateOneMenu(i, subMenu);\r
459         }\r
460         DrawMenuBar(hwndMain);\r
461     }\r
462 \r
463     if(!addLanguage) return;\r
464     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
467         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
468         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
469         i = 0; lastChecked = IDM_English;\r
470         do {\r
471             char *p, *q = fileData.cFileName;\r
472             int checkFlag = MF_UNCHECKED;\r
473             languageFile[i] = strdup(q);\r
474             if(barbaric && !strcmp(oldLanguage, q)) {\r
475                 checkFlag = MF_CHECKED;\r
476                 lastChecked = IDM_English + i + 1;\r
477                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
478             }\r
479             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
480             p = strstr(fileData.cFileName, ".lng");\r
481             if(p) *p = 0;\r
482             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
483         } while(FindNextFile(hFind, &fileData));\r
484         FindClose(hFind);\r
485     }\r
486 }\r
487 \r
488 #endif\r
489 \r
490 typedef struct {\r
491   char *name;\r
492   int squareSize;\r
493   int lineGap;\r
494   int smallLayout;\r
495   int tinyLayout;\r
496   int cliWidth, cliHeight;\r
497 } SizeInfo;\r
498 \r
499 SizeInfo sizeInfo[] = \r
500 {\r
501   { "tiny",     21, 0, 1, 1, 0, 0 },\r
502   { "teeny",    25, 1, 1, 1, 0, 0 },\r
503   { "dinky",    29, 1, 1, 1, 0, 0 },\r
504   { "petite",   33, 1, 1, 1, 0, 0 },\r
505   { "slim",     37, 2, 1, 0, 0, 0 },\r
506   { "small",    40, 2, 1, 0, 0, 0 },\r
507   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
508   { "middling", 49, 2, 0, 0, 0, 0 },\r
509   { "average",  54, 2, 0, 0, 0, 0 },\r
510   { "moderate", 58, 3, 0, 0, 0, 0 },\r
511   { "medium",   64, 3, 0, 0, 0, 0 },\r
512   { "bulky",    72, 3, 0, 0, 0, 0 },\r
513   { "large",    80, 3, 0, 0, 0, 0 },\r
514   { "big",      87, 3, 0, 0, 0, 0 },\r
515   { "huge",     95, 3, 0, 0, 0, 0 },\r
516   { "giant",    108, 3, 0, 0, 0, 0 },\r
517   { "colossal", 116, 4, 0, 0, 0, 0 },\r
518   { "titanic",  129, 4, 0, 0, 0, 0 },\r
519   { NULL, 0, 0, 0, 0, 0, 0 }\r
520 };\r
521 \r
522 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
523 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
524 {\r
525   { 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
526   { 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
527   { 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
528   { 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
529   { 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
530   { 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
531   { 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
532   { 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
533   { 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
534   { 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
535   { 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
536   { 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
537   { 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
538   { 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
539   { 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
540   { 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
541   { 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
542   { 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
543 };\r
544 \r
545 MyFont *font[NUM_SIZES][NUM_FONTS];\r
546 \r
547 typedef struct {\r
548   char *label;\r
549   int id;\r
550   HWND hwnd;\r
551   WNDPROC wndproc;\r
552 } MyButtonDesc;\r
553 \r
554 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
555 #define N_BUTTONS 5\r
556 \r
557 MyButtonDesc buttonDesc[N_BUTTONS] =\r
558 {\r
559   {"<<", IDM_ToStart, NULL, NULL},\r
560   {"<", IDM_Backward, NULL, NULL},\r
561   {"P", IDM_Pause, NULL, NULL},\r
562   {">", IDM_Forward, NULL, NULL},\r
563   {">>", IDM_ToEnd, NULL, NULL},\r
564 };\r
565 \r
566 int tinyLayout = 0, smallLayout = 0;\r
567 #define MENU_BAR_ITEMS 9\r
568 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
569   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
570   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
571 };\r
572 \r
573 \r
574 MySound sounds[(int)NSoundClasses];\r
575 MyTextAttribs textAttribs[(int)NColorClasses];\r
576 \r
577 MyColorizeAttribs colorizeAttribs[] = {\r
578   { (COLORREF)0, 0, N_("Shout Text") },\r
579   { (COLORREF)0, 0, N_("SShout/CShout") },\r
580   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
581   { (COLORREF)0, 0, N_("Channel Text") },\r
582   { (COLORREF)0, 0, N_("Kibitz Text") },\r
583   { (COLORREF)0, 0, N_("Tell Text") },\r
584   { (COLORREF)0, 0, N_("Challenge Text") },\r
585   { (COLORREF)0, 0, N_("Request Text") },\r
586   { (COLORREF)0, 0, N_("Seek Text") },\r
587   { (COLORREF)0, 0, N_("Normal Text") },\r
588   { (COLORREF)0, 0, N_("None") }\r
589 };\r
590 \r
591 \r
592 \r
593 static char *commentTitle;\r
594 static char *commentText;\r
595 static int commentIndex;\r
596 static Boolean editComment = FALSE;\r
597 \r
598 \r
599 char errorTitle[MSG_SIZ];\r
600 char errorMessage[2*MSG_SIZ];\r
601 HWND errorDialog = NULL;\r
602 BOOLEAN moveErrorMessageUp = FALSE;\r
603 BOOLEAN consoleEcho = TRUE;\r
604 CHARFORMAT consoleCF;\r
605 COLORREF consoleBackgroundColor;\r
606 \r
607 char *programVersion;\r
608 \r
609 #define CPReal 1\r
610 #define CPComm 2\r
611 #define CPSock 3\r
612 #define CPRcmd 4\r
613 typedef int CPKind;\r
614 \r
615 typedef struct {\r
616   CPKind kind;\r
617   HANDLE hProcess;\r
618   DWORD pid;\r
619   HANDLE hTo;\r
620   HANDLE hFrom;\r
621   SOCKET sock;\r
622   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
623 } ChildProc;\r
624 \r
625 #define INPUT_SOURCE_BUF_SIZE 4096\r
626 \r
627 typedef struct _InputSource {\r
628   CPKind kind;\r
629   HANDLE hFile;\r
630   SOCKET sock;\r
631   int lineByLine;\r
632   HANDLE hThread;\r
633   DWORD id;\r
634   char buf[INPUT_SOURCE_BUF_SIZE];\r
635   char *next;\r
636   DWORD count;\r
637   int error;\r
638   InputCallback func;\r
639   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
640   VOIDSTAR closure;\r
641 } InputSource;\r
642 \r
643 InputSource *consoleInputSource;\r
644 \r
645 DCB dcb;\r
646 \r
647 /* forward */\r
648 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
649 VOID ConsoleCreate();\r
650 LRESULT CALLBACK\r
651   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
652 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
653 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
654 VOID ParseCommSettings(char *arg, DCB *dcb);\r
655 LRESULT CALLBACK\r
656   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
657 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
658 void ParseIcsTextMenu(char *icsTextMenuString);\r
659 VOID PopUpNameDialog(char firstchar);\r
660 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
661 \r
662 /* [AS] */\r
663 int NewGameFRC();\r
664 int GameListOptions();\r
665 \r
666 int dummy; // [HGM] for obsolete args\r
667 \r
668 HWND hwndMain = NULL;        /* root window*/\r
669 HWND hwndConsole = NULL;\r
670 HWND commentDialog = NULL;\r
671 HWND moveHistoryDialog = NULL;\r
672 HWND evalGraphDialog = NULL;\r
673 HWND engineOutputDialog = NULL;\r
674 HWND gameListDialog = NULL;\r
675 HWND editTagsDialog = NULL;\r
676 \r
677 int commentUp = FALSE;\r
678 \r
679 WindowPlacement wpMain;\r
680 WindowPlacement wpConsole;\r
681 WindowPlacement wpComment;\r
682 WindowPlacement wpMoveHistory;\r
683 WindowPlacement wpEvalGraph;\r
684 WindowPlacement wpEngineOutput;\r
685 WindowPlacement wpGameList;\r
686 WindowPlacement wpTags;\r
687 \r
688 VOID EngineOptionsPopup(); // [HGM] settings\r
689 \r
690 VOID GothicPopUp(char *title, VariantClass variant);\r
691 /*\r
692  * Setting "frozen" should disable all user input other than deleting\r
693  * the window.  We do this while engines are initializing themselves.\r
694  */\r
695 static int frozen = 0;\r
696 static int oldMenuItemState[MENU_BAR_ITEMS];\r
697 void FreezeUI()\r
698 {\r
699   HMENU hmenu;\r
700   int i;\r
701 \r
702   if (frozen) return;\r
703   frozen = 1;\r
704   hmenu = GetMenu(hwndMain);\r
705   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
706     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
707   }\r
708   DrawMenuBar(hwndMain);\r
709 }\r
710 \r
711 /* Undo a FreezeUI */\r
712 void ThawUI()\r
713 {\r
714   HMENU hmenu;\r
715   int i;\r
716 \r
717   if (!frozen) return;\r
718   frozen = 0;\r
719   hmenu = GetMenu(hwndMain);\r
720   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
721     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
722   }\r
723   DrawMenuBar(hwndMain);\r
724 }\r
725 \r
726 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
727 \r
728 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
729 #ifdef JAWS\r
730 #include "jaws.c"\r
731 #else\r
732 #define JAWS_INIT\r
733 #define JAWS_ARGS\r
734 #define JAWS_ALT_INTERCEPT\r
735 #define JAWS_KB_NAVIGATION\r
736 #define JAWS_MENU_ITEMS\r
737 #define JAWS_SILENCE\r
738 #define JAWS_REPLAY\r
739 #define JAWS_ACCEL\r
740 #define JAWS_COPYRIGHT\r
741 #define JAWS_DELETE(X) X\r
742 #define SAYMACHINEMOVE()\r
743 #define SAY(X)\r
744 #endif\r
745 \r
746 /*---------------------------------------------------------------------------*\\r
747  *\r
748  * WinMain\r
749  *\r
750 \*---------------------------------------------------------------------------*/\r
751 \r
752 int APIENTRY\r
753 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
754         LPSTR lpCmdLine, int nCmdShow)\r
755 {\r
756   MSG msg;\r
757   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
758 //  INITCOMMONCONTROLSEX ex;\r
759 \r
760   debugFP = stderr;\r
761 \r
762   LoadLibrary("RICHED32.DLL");\r
763   consoleCF.cbSize = sizeof(CHARFORMAT);\r
764 \r
765   if (!InitApplication(hInstance)) {\r
766     return (FALSE);\r
767   }\r
768   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
769     return (FALSE);\r
770   }\r
771 \r
772   JAWS_INIT\r
773   TranslateMenus(1);\r
774 \r
775 //  InitCommonControlsEx(&ex);\r
776   InitCommonControls();\r
777 \r
778   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
779   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
780   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
781 \r
782   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
783 \r
784   while (GetMessage(&msg, /* message structure */\r
785                     NULL, /* handle of window receiving the message */\r
786                     0,    /* lowest message to examine */\r
787                     0))   /* highest message to examine */\r
788     {\r
789 \r
790       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
791         // [HGM] navigate: switch between all windows with tab\r
792         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
793         int i, currentElement = 0;\r
794 \r
795         // first determine what element of the chain we come from (if any)\r
796         if(appData.icsActive) {\r
797             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
798             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
799         }\r
800         if(engineOutputDialog && EngineOutputIsUp()) {\r
801             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
802             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
803         }\r
804         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
805             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
806         }\r
807         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
808         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
809         if(msg.hwnd == e1)                 currentElement = 2; else\r
810         if(msg.hwnd == e2)                 currentElement = 3; else\r
811         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
812         if(msg.hwnd == mh)                currentElement = 4; else\r
813         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
814         if(msg.hwnd == hText)  currentElement = 5; else\r
815         if(msg.hwnd == hInput) currentElement = 6; else\r
816         for (i = 0; i < N_BUTTONS; i++) {\r
817             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
818         }\r
819 \r
820         // determine where to go to\r
821         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
822           do {\r
823             currentElement = (currentElement + direction) % 7;\r
824             switch(currentElement) {\r
825                 case 0:\r
826                   h = hwndMain; break; // passing this case always makes the loop exit\r
827                 case 1:\r
828                   h = buttonDesc[0].hwnd; break; // could be NULL\r
829                 case 2:\r
830                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
831                   h = e1; break;\r
832                 case 3:\r
833                   if(!EngineOutputIsUp()) continue;\r
834                   h = e2; break;\r
835                 case 4:\r
836                   if(!MoveHistoryIsUp()) continue;\r
837                   h = mh; break;\r
838 //              case 6: // input to eval graph does not seem to get here!\r
839 //                if(!EvalGraphIsUp()) continue;\r
840 //                h = evalGraphDialog; break;\r
841                 case 5:\r
842                   if(!appData.icsActive) continue;\r
843                   SAY("display");\r
844                   h = hText; break;\r
845                 case 6:\r
846                   if(!appData.icsActive) continue;\r
847                   SAY("input");\r
848                   h = hInput; break;\r
849             }\r
850           } while(h == 0);\r
851 \r
852           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
853           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
854           SetFocus(h);\r
855 \r
856           continue; // this message now has been processed\r
857         }\r
858       }\r
859 \r
860       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
861           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
862           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
863           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
864           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
865           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
866           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
867           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
868           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
869           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
870         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
871         for(i=0; i<MAX_CHAT; i++) \r
872             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
873                 done = 1; break;\r
874         }\r
875         if(done) continue; // [HGM] chat: end patch\r
876         TranslateMessage(&msg); /* Translates virtual key codes */\r
877         DispatchMessage(&msg);  /* Dispatches message to window */\r
878       }\r
879     }\r
880 \r
881 \r
882   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
883 }\r
884 \r
885 /*---------------------------------------------------------------------------*\\r
886  *\r
887  * Initialization functions\r
888  *\r
889 \*---------------------------------------------------------------------------*/\r
890 \r
891 void\r
892 SetUserLogo()\r
893 {   // update user logo if necessary\r
894     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
895 \r
896     if(appData.autoLogo) {\r
897           curName = UserName();\r
898           if(strcmp(curName, oldUserName)) {\r
899                 GetCurrentDirectory(MSG_SIZ, dir);\r
900                 SetCurrentDirectory(installDir);\r
901                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
902                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
903                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
904                 if(userLogo == NULL)\r
905                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
906                 SetCurrentDirectory(dir); /* return to prev directory */\r
907           }\r
908     }\r
909 }\r
910 \r
911 BOOL\r
912 InitApplication(HINSTANCE hInstance)\r
913 {\r
914   WNDCLASS wc;\r
915 \r
916   /* Fill in window class structure with parameters that describe the */\r
917   /* main window. */\r
918 \r
919   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
920   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
921   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
922   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
923   wc.hInstance     = hInstance;         /* Owner of this class */\r
924   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
925   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
926   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
927   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
928   wc.lpszClassName = szAppName;                 /* Name to register as */\r
929 \r
930   /* Register the window class and return success/failure code. */\r
931   if (!RegisterClass(&wc)) return FALSE;\r
932 \r
933   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
934   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
935   wc.cbClsExtra    = 0;\r
936   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
937   wc.hInstance     = hInstance;\r
938   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
939   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
940   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
941   wc.lpszMenuName  = NULL;\r
942   wc.lpszClassName = szConsoleName;\r
943 \r
944   if (!RegisterClass(&wc)) return FALSE;\r
945   return TRUE;\r
946 }\r
947 \r
948 \r
949 /* Set by InitInstance, used by EnsureOnScreen */\r
950 int screenHeight, screenWidth;\r
951 \r
952 void\r
953 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
954 {\r
955 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
956   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
957   if (*x > screenWidth - 32) *x = 0;\r
958   if (*y > screenHeight - 32) *y = 0;\r
959   if (*x < minX) *x = minX;\r
960   if (*y < minY) *y = minY;\r
961 }\r
962 \r
963 VOID\r
964 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
965 {\r
966   char buf[MSG_SIZ], dir[MSG_SIZ];\r
967   GetCurrentDirectory(MSG_SIZ, dir);\r
968   SetCurrentDirectory(installDir);\r
969   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
970       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
971 \r
972       if (cps->programLogo == NULL && appData.debugMode) {\r
973           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
974       }\r
975   } else if(appData.autoLogo) {\r
976       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
977         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
978         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
979       } else\r
980       if(appData.directory[n] && appData.directory[n][0]) {\r
981         SetCurrentDirectory(appData.directory[n]);\r
982         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
983       }\r
984   }\r
985   SetCurrentDirectory(dir); /* return to prev directory */\r
986 }\r
987 \r
988 VOID\r
989 InitTextures()\r
990 {\r
991   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
992   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
993   \r
994   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
995       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
996       liteBackTextureMode = appData.liteBackTextureMode;\r
997 \r
998       if (liteBackTexture == NULL && appData.debugMode) {\r
999           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1000       }\r
1001   }\r
1002   \r
1003   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1004       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1005       darkBackTextureMode = appData.darkBackTextureMode;\r
1006 \r
1007       if (darkBackTexture == NULL && appData.debugMode) {\r
1008           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1009       }\r
1010   }\r
1011 }\r
1012 \r
1013 BOOL\r
1014 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1015 {\r
1016   HWND hwnd; /* Main window handle. */\r
1017   int ibs;\r
1018   WINDOWPLACEMENT wp;\r
1019   char *filepart;\r
1020 \r
1021   hInst = hInstance;    /* Store instance handle in our global variable */\r
1022   programName = szAppName;\r
1023 \r
1024   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1025     *filepart = NULLCHAR;\r
1026   } else {\r
1027     GetCurrentDirectory(MSG_SIZ, installDir);\r
1028   }\r
1029   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1030   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1031   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1032   /* xboard, and older WinBoards, controlled the move sound with the\r
1033      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1034      always turn the option on (so that the backend will call us),\r
1035      then let the user turn the sound off by setting it to silence if\r
1036      desired.  To accommodate old winboard.ini files saved by old\r
1037      versions of WinBoard, we also turn off the sound if the option\r
1038      was initially set to false. [HGM] taken out of InitAppData */\r
1039   if (!appData.ringBellAfterMoves) {\r
1040     sounds[(int)SoundMove].name = strdup("");\r
1041     appData.ringBellAfterMoves = TRUE;\r
1042   }\r
1043   if (appData.debugMode) {\r
1044     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1045     setbuf(debugFP, NULL);\r
1046   }\r
1047 \r
1048   LoadLanguageFile(appData.language);\r
1049 \r
1050   InitBackEnd1();\r
1051 \r
1052 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1053 //  InitEngineUCI( installDir, &second );\r
1054 \r
1055   /* Create a main window for this application instance. */\r
1056   hwnd = CreateWindow(szAppName, szTitle,\r
1057                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1058                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1059                       NULL, NULL, hInstance, NULL);\r
1060   hwndMain = hwnd;\r
1061 \r
1062   /* If window could not be created, return "failure" */\r
1063   if (!hwnd) {\r
1064     return (FALSE);\r
1065   }\r
1066 \r
1067   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1068   LoadLogo(&first, 0, FALSE);\r
1069   LoadLogo(&second, 1, appData.icsActive);\r
1070 \r
1071   SetUserLogo();\r
1072 \r
1073   iconWhite = LoadIcon(hInstance, "icon_white");\r
1074   iconBlack = LoadIcon(hInstance, "icon_black");\r
1075   iconCurrent = iconWhite;\r
1076   InitDrawingColors();\r
1077   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1078   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1079   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1080     /* Compute window size for each board size, and use the largest\r
1081        size that fits on this screen as the default. */\r
1082     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1083     if (boardSize == (BoardSize)-1 &&\r
1084         winH <= screenHeight\r
1085            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1086         && winW <= screenWidth) {\r
1087       boardSize = (BoardSize)ibs;\r
1088     }\r
1089   }\r
1090 \r
1091   InitDrawingSizes(boardSize, 0);\r
1092   InitMenuChecks();\r
1093   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1094 \r
1095   /* [AS] Load textures if specified */\r
1096   InitTextures();\r
1097 \r
1098   mysrandom( (unsigned) time(NULL) );\r
1099 \r
1100   /* [AS] Restore layout */\r
1101   if( wpMoveHistory.visible ) {\r
1102       MoveHistoryPopUp();\r
1103   }\r
1104 \r
1105   if( wpEvalGraph.visible ) {\r
1106       EvalGraphPopUp();\r
1107   }\r
1108 \r
1109   if( wpEngineOutput.visible ) {\r
1110       EngineOutputPopUp();\r
1111   }\r
1112 \r
1113   /* Make the window visible; update its client area; and return "success" */\r
1114   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1115   wp.length = sizeof(WINDOWPLACEMENT);\r
1116   wp.flags = 0;\r
1117   wp.showCmd = nCmdShow;\r
1118   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1119   wp.rcNormalPosition.left = wpMain.x;\r
1120   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1121   wp.rcNormalPosition.top = wpMain.y;\r
1122   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1123   SetWindowPlacement(hwndMain, &wp);\r
1124 \r
1125   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1126 \r
1127   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1128                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1129 \r
1130   if (hwndConsole) {\r
1131 #if AOT_CONSOLE\r
1132     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1133                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1134 #endif\r
1135     ShowWindow(hwndConsole, nCmdShow);\r
1136     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1137       char buf[MSG_SIZ], *p = buf, *q;\r
1138         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1139       do {\r
1140         q = strchr(p, ';');\r
1141         if(q) *q++ = 0;\r
1142         if(*p) ChatPopUp(p);\r
1143       } while(p=q);\r
1144     }\r
1145     SetActiveWindow(hwndConsole);\r
1146   }\r
1147   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1148   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1149 \r
1150   return TRUE;\r
1151 \r
1152 }\r
1153 \r
1154 VOID\r
1155 InitMenuChecks()\r
1156 {\r
1157   HMENU hmenu = GetMenu(hwndMain);\r
1158 \r
1159   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1160                         MF_BYCOMMAND|((appData.icsActive &&\r
1161                                        *appData.icsCommPort != NULLCHAR) ?\r
1162                                       MF_ENABLED : MF_GRAYED));\r
1163   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1164                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1165                                      MF_CHECKED : MF_UNCHECKED));\r
1166 }\r
1167 \r
1168 //---------------------------------------------------------------------------------------------------------\r
1169 \r
1170 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1171 #define XBOARD FALSE\r
1172 \r
1173 #define OPTCHAR "/"\r
1174 #define SEPCHAR "="\r
1175 \r
1176 #include "args.h"\r
1177 \r
1178 // front-end part of option handling\r
1179 \r
1180 VOID\r
1181 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1182 {\r
1183   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1184   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1185   DeleteDC(hdc);\r
1186   lf->lfWidth = 0;\r
1187   lf->lfEscapement = 0;\r
1188   lf->lfOrientation = 0;\r
1189   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1190   lf->lfItalic = mfp->italic;\r
1191   lf->lfUnderline = mfp->underline;\r
1192   lf->lfStrikeOut = mfp->strikeout;\r
1193   lf->lfCharSet = mfp->charset;\r
1194   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1195   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1196   lf->lfQuality = DEFAULT_QUALITY;\r
1197   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1198     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1199 }\r
1200 \r
1201 void\r
1202 CreateFontInMF(MyFont *mf)\r
1203\r
1204   LFfromMFP(&mf->lf, &mf->mfp);\r
1205   if (mf->hf) DeleteObject(mf->hf);\r
1206   mf->hf = CreateFontIndirect(&mf->lf);\r
1207 }\r
1208 \r
1209 // [HGM] This platform-dependent table provides the location for storing the color info\r
1210 void *\r
1211 colorVariable[] = {\r
1212   &whitePieceColor, \r
1213   &blackPieceColor, \r
1214   &lightSquareColor,\r
1215   &darkSquareColor, \r
1216   &highlightSquareColor,\r
1217   &premoveHighlightColor,\r
1218   NULL,\r
1219   &consoleBackgroundColor,\r
1220   &appData.fontForeColorWhite,\r
1221   &appData.fontBackColorWhite,\r
1222   &appData.fontForeColorBlack,\r
1223   &appData.fontBackColorBlack,\r
1224   &appData.evalHistColorWhite,\r
1225   &appData.evalHistColorBlack,\r
1226   &appData.highlightArrowColor,\r
1227 };\r
1228 \r
1229 /* Command line font name parser.  NULL name means do nothing.\r
1230    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1231    For backward compatibility, syntax without the colon is also\r
1232    accepted, but font names with digits in them won't work in that case.\r
1233 */\r
1234 VOID\r
1235 ParseFontName(char *name, MyFontParams *mfp)\r
1236 {\r
1237   char *p, *q;\r
1238   if (name == NULL) return;\r
1239   p = name;\r
1240   q = strchr(p, ':');\r
1241   if (q) {\r
1242     if (q - p >= sizeof(mfp->faceName))\r
1243       ExitArgError(_("Font name too long:"), name, TRUE);\r
1244     memcpy(mfp->faceName, p, q - p);\r
1245     mfp->faceName[q - p] = NULLCHAR;\r
1246     p = q + 1;\r
1247   } else {\r
1248     q = mfp->faceName;\r
1249     while (*p && !isdigit(*p)) {\r
1250       *q++ = *p++;\r
1251       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1252         ExitArgError(_("Font name too long:"), name, TRUE);\r
1253     }\r
1254     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1255     *q = NULLCHAR;\r
1256   }\r
1257   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1258   mfp->pointSize = (float) atof(p);\r
1259   mfp->bold = (strchr(p, 'b') != NULL);\r
1260   mfp->italic = (strchr(p, 'i') != NULL);\r
1261   mfp->underline = (strchr(p, 'u') != NULL);\r
1262   mfp->strikeout = (strchr(p, 's') != NULL);\r
1263   mfp->charset = DEFAULT_CHARSET;\r
1264   q = strchr(p, 'c');\r
1265   if (q)\r
1266     mfp->charset = (BYTE) atoi(q+1);\r
1267 }\r
1268 \r
1269 void\r
1270 ParseFont(char *name, int number)\r
1271 { // wrapper to shield back-end from 'font'\r
1272   ParseFontName(name, &font[boardSize][number]->mfp);\r
1273 }\r
1274 \r
1275 void\r
1276 SetFontDefaults()\r
1277 { // in WB  we have a 2D array of fonts; this initializes their description\r
1278   int i, j;\r
1279   /* Point font array elements to structures and\r
1280      parse default font names */\r
1281   for (i=0; i<NUM_FONTS; i++) {\r
1282     for (j=0; j<NUM_SIZES; j++) {\r
1283       font[j][i] = &fontRec[j][i];\r
1284       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1285     }\r
1286   }\r
1287 }\r
1288 \r
1289 void\r
1290 CreateFonts()\r
1291 { // here we create the actual fonts from the selected descriptions\r
1292   int i, j;\r
1293   for (i=0; i<NUM_FONTS; i++) {\r
1294     for (j=0; j<NUM_SIZES; j++) {\r
1295       CreateFontInMF(font[j][i]);\r
1296     }\r
1297   }\r
1298 }\r
1299 /* Color name parser.\r
1300    X version accepts X color names, but this one\r
1301    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1302 COLORREF\r
1303 ParseColorName(char *name)\r
1304 {\r
1305   int red, green, blue, count;\r
1306   char buf[MSG_SIZ];\r
1307 \r
1308   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1309   if (count != 3) {\r
1310     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1311       &red, &green, &blue);\r
1312   }\r
1313   if (count != 3) {\r
1314     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1315     DisplayError(buf, 0);\r
1316     return RGB(0, 0, 0);\r
1317   }\r
1318   return PALETTERGB(red, green, blue);\r
1319 }\r
1320 \r
1321 void\r
1322 ParseColor(int n, char *name)\r
1323 { // for WinBoard the color is an int, which needs to be derived from the string\r
1324   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1325 }\r
1326 \r
1327 void\r
1328 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1329 {\r
1330   char *e = argValue;\r
1331   int eff = 0;\r
1332 \r
1333   while (*e) {\r
1334     if (*e == 'b')      eff |= CFE_BOLD;\r
1335     else if (*e == 'i') eff |= CFE_ITALIC;\r
1336     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1337     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1338     else if (*e == '#' || isdigit(*e)) break;\r
1339     e++;\r
1340   }\r
1341   *effects = eff;\r
1342   *color   = ParseColorName(e);\r
1343 }\r
1344 \r
1345 void\r
1346 ParseTextAttribs(ColorClass cc, char *s)\r
1347 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1348     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1349     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1350 }\r
1351 \r
1352 void\r
1353 ParseBoardSize(void *addr, char *name)\r
1354 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1355   BoardSize bs = SizeTiny;\r
1356   while (sizeInfo[bs].name != NULL) {\r
1357     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1358         *(BoardSize *)addr = bs;\r
1359         return;\r
1360     }\r
1361     bs++;\r
1362   }\r
1363   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1364 }\r
1365 \r
1366 void\r
1367 LoadAllSounds()\r
1368 { // [HGM] import name from appData first\r
1369   ColorClass cc;\r
1370   SoundClass sc;\r
1371   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1372     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1373     textAttribs[cc].sound.data = NULL;\r
1374     MyLoadSound(&textAttribs[cc].sound);\r
1375   }\r
1376   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1377     textAttribs[cc].sound.name = strdup("");\r
1378     textAttribs[cc].sound.data = NULL;\r
1379   }\r
1380   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1381     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1382     sounds[sc].data = NULL;\r
1383     MyLoadSound(&sounds[sc]);\r
1384   }\r
1385 }\r
1386 \r
1387 void\r
1388 SetCommPortDefaults()\r
1389 {\r
1390    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1391   dcb.DCBlength = sizeof(DCB);\r
1392   dcb.BaudRate = 9600;\r
1393   dcb.fBinary = TRUE;\r
1394   dcb.fParity = FALSE;\r
1395   dcb.fOutxCtsFlow = FALSE;\r
1396   dcb.fOutxDsrFlow = FALSE;\r
1397   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1398   dcb.fDsrSensitivity = FALSE;\r
1399   dcb.fTXContinueOnXoff = TRUE;\r
1400   dcb.fOutX = FALSE;\r
1401   dcb.fInX = FALSE;\r
1402   dcb.fNull = FALSE;\r
1403   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1404   dcb.fAbortOnError = FALSE;\r
1405   dcb.ByteSize = 7;\r
1406   dcb.Parity = SPACEPARITY;\r
1407   dcb.StopBits = ONESTOPBIT;\r
1408 }\r
1409 \r
1410 // [HGM] args: these three cases taken out to stay in front-end\r
1411 void\r
1412 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1413 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1414         // while the curent board size determines the element. This system should be ported to XBoard.\r
1415         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1416         int bs;\r
1417         for (bs=0; bs<NUM_SIZES; bs++) {\r
1418           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1419           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1420           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1421             ad->argName, mfp->faceName, mfp->pointSize,\r
1422             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1423             mfp->bold ? "b" : "",\r
1424             mfp->italic ? "i" : "",\r
1425             mfp->underline ? "u" : "",\r
1426             mfp->strikeout ? "s" : "",\r
1427             (int)mfp->charset);\r
1428         }\r
1429       }\r
1430 \r
1431 void\r
1432 ExportSounds()\r
1433 { // [HGM] copy the names from the internal WB variables to appData\r
1434   ColorClass cc;\r
1435   SoundClass sc;\r
1436   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1437     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1438   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1439     (&appData.soundMove)[sc] = sounds[sc].name;\r
1440 }\r
1441 \r
1442 void\r
1443 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1444 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1445         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1446         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1447           (ta->effects & CFE_BOLD) ? "b" : "",\r
1448           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1449           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1450           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1451           (ta->effects) ? " " : "",\r
1452           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1453       }\r
1454 \r
1455 void\r
1456 SaveColor(FILE *f, ArgDescriptor *ad)\r
1457 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1458         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1459         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1460           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1461 }\r
1462 \r
1463 void\r
1464 SaveBoardSize(FILE *f, char *name, void *addr)\r
1465 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1466   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1467 }\r
1468 \r
1469 void\r
1470 ParseCommPortSettings(char *s)\r
1471 { // wrapper to keep dcb from back-end\r
1472   ParseCommSettings(s, &dcb);\r
1473 }\r
1474 \r
1475 void\r
1476 GetWindowCoords()\r
1477 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1478   GetActualPlacement(hwndMain, &wpMain);\r
1479   GetActualPlacement(hwndConsole, &wpConsole);\r
1480   GetActualPlacement(commentDialog, &wpComment);\r
1481   GetActualPlacement(editTagsDialog, &wpTags);\r
1482   GetActualPlacement(gameListDialog, &wpGameList);\r
1483   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1484   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1485   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1486 }\r
1487 \r
1488 void\r
1489 PrintCommPortSettings(FILE *f, char *name)\r
1490 { // wrapper to shield back-end from DCB\r
1491       PrintCommSettings(f, name, &dcb);\r
1492 }\r
1493 \r
1494 int\r
1495 MySearchPath(char *installDir, char *name, char *fullname)\r
1496 {\r
1497   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1498   if(name[0]== '%') {\r
1499     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1500     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1501       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1502       *strchr(buf, '%') = 0;\r
1503       strcat(fullname, getenv(buf));\r
1504       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1505     }\r
1506     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1507     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1508     return (int) strlen(fullname);\r
1509   }\r
1510   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1511 }\r
1512 \r
1513 int\r
1514 MyGetFullPathName(char *name, char *fullname)\r
1515 {\r
1516   char *dummy;\r
1517   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1518 }\r
1519 \r
1520 int\r
1521 MainWindowUp()\r
1522 { // [HGM] args: allows testing if main window is realized from back-end\r
1523   return hwndMain != NULL;\r
1524 }\r
1525 \r
1526 void\r
1527 PopUpStartupDialog()\r
1528 {\r
1529     FARPROC lpProc;\r
1530     \r
1531     LoadLanguageFile(appData.language);\r
1532     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1533     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1534     FreeProcInstance(lpProc);\r
1535 }\r
1536 \r
1537 /*---------------------------------------------------------------------------*\\r
1538  *\r
1539  * GDI board drawing routines\r
1540  *\r
1541 \*---------------------------------------------------------------------------*/\r
1542 \r
1543 /* [AS] Draw square using background texture */\r
1544 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1545 {\r
1546     XFORM   x;\r
1547 \r
1548     if( mode == 0 ) {\r
1549         return; /* Should never happen! */\r
1550     }\r
1551 \r
1552     SetGraphicsMode( dst, GM_ADVANCED );\r
1553 \r
1554     switch( mode ) {\r
1555     case 1:\r
1556         /* Identity */\r
1557         break;\r
1558     case 2:\r
1559         /* X reflection */\r
1560         x.eM11 = -1.0;\r
1561         x.eM12 = 0;\r
1562         x.eM21 = 0;\r
1563         x.eM22 = 1.0;\r
1564         x.eDx = (FLOAT) dw + dx - 1;\r
1565         x.eDy = 0;\r
1566         dx = 0;\r
1567         SetWorldTransform( dst, &x );\r
1568         break;\r
1569     case 3:\r
1570         /* Y reflection */\r
1571         x.eM11 = 1.0;\r
1572         x.eM12 = 0;\r
1573         x.eM21 = 0;\r
1574         x.eM22 = -1.0;\r
1575         x.eDx = 0;\r
1576         x.eDy = (FLOAT) dh + dy - 1;\r
1577         dy = 0;\r
1578         SetWorldTransform( dst, &x );\r
1579         break;\r
1580     case 4:\r
1581         /* X/Y flip */\r
1582         x.eM11 = 0;\r
1583         x.eM12 = 1.0;\r
1584         x.eM21 = 1.0;\r
1585         x.eM22 = 0;\r
1586         x.eDx = (FLOAT) dx;\r
1587         x.eDy = (FLOAT) dy;\r
1588         dx = 0;\r
1589         dy = 0;\r
1590         SetWorldTransform( dst, &x );\r
1591         break;\r
1592     }\r
1593 \r
1594     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1595 \r
1596     x.eM11 = 1.0;\r
1597     x.eM12 = 0;\r
1598     x.eM21 = 0;\r
1599     x.eM22 = 1.0;\r
1600     x.eDx = 0;\r
1601     x.eDy = 0;\r
1602     SetWorldTransform( dst, &x );\r
1603 \r
1604     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1605 }\r
1606 \r
1607 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1608 enum {\r
1609     PM_WP = (int) WhitePawn, \r
1610     PM_WN = (int) WhiteKnight, \r
1611     PM_WB = (int) WhiteBishop, \r
1612     PM_WR = (int) WhiteRook, \r
1613     PM_WQ = (int) WhiteQueen, \r
1614     PM_WF = (int) WhiteFerz, \r
1615     PM_WW = (int) WhiteWazir, \r
1616     PM_WE = (int) WhiteAlfil, \r
1617     PM_WM = (int) WhiteMan, \r
1618     PM_WO = (int) WhiteCannon, \r
1619     PM_WU = (int) WhiteUnicorn, \r
1620     PM_WH = (int) WhiteNightrider, \r
1621     PM_WA = (int) WhiteAngel, \r
1622     PM_WC = (int) WhiteMarshall, \r
1623     PM_WAB = (int) WhiteCardinal, \r
1624     PM_WD = (int) WhiteDragon, \r
1625     PM_WL = (int) WhiteLance, \r
1626     PM_WS = (int) WhiteCobra, \r
1627     PM_WV = (int) WhiteFalcon, \r
1628     PM_WSG = (int) WhiteSilver, \r
1629     PM_WG = (int) WhiteGrasshopper, \r
1630     PM_WK = (int) WhiteKing,\r
1631     PM_BP = (int) BlackPawn, \r
1632     PM_BN = (int) BlackKnight, \r
1633     PM_BB = (int) BlackBishop, \r
1634     PM_BR = (int) BlackRook, \r
1635     PM_BQ = (int) BlackQueen, \r
1636     PM_BF = (int) BlackFerz, \r
1637     PM_BW = (int) BlackWazir, \r
1638     PM_BE = (int) BlackAlfil, \r
1639     PM_BM = (int) BlackMan,\r
1640     PM_BO = (int) BlackCannon, \r
1641     PM_BU = (int) BlackUnicorn, \r
1642     PM_BH = (int) BlackNightrider, \r
1643     PM_BA = (int) BlackAngel, \r
1644     PM_BC = (int) BlackMarshall, \r
1645     PM_BG = (int) BlackGrasshopper, \r
1646     PM_BAB = (int) BlackCardinal,\r
1647     PM_BD = (int) BlackDragon,\r
1648     PM_BL = (int) BlackLance,\r
1649     PM_BS = (int) BlackCobra,\r
1650     PM_BV = (int) BlackFalcon,\r
1651     PM_BSG = (int) BlackSilver,\r
1652     PM_BK = (int) BlackKing\r
1653 };\r
1654 \r
1655 static HFONT hPieceFont = NULL;\r
1656 static HBITMAP hPieceMask[(int) EmptySquare];\r
1657 static HBITMAP hPieceFace[(int) EmptySquare];\r
1658 static int fontBitmapSquareSize = 0;\r
1659 static char pieceToFontChar[(int) EmptySquare] =\r
1660                               { 'p', 'n', 'b', 'r', 'q', \r
1661                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1662                       'k', 'o', 'm', 'v', 't', 'w', \r
1663                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1664                                                               'l' };\r
1665 \r
1666 extern BOOL SetCharTable( char *table, const char * map );\r
1667 /* [HGM] moved to backend.c */\r
1668 \r
1669 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1670 {\r
1671     HBRUSH hbrush;\r
1672     BYTE r1 = GetRValue( color );\r
1673     BYTE g1 = GetGValue( color );\r
1674     BYTE b1 = GetBValue( color );\r
1675     BYTE r2 = r1 / 2;\r
1676     BYTE g2 = g1 / 2;\r
1677     BYTE b2 = b1 / 2;\r
1678     RECT rc;\r
1679 \r
1680     /* Create a uniform background first */\r
1681     hbrush = CreateSolidBrush( color );\r
1682     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1683     FillRect( hdc, &rc, hbrush );\r
1684     DeleteObject( hbrush );\r
1685     \r
1686     if( mode == 1 ) {\r
1687         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1688         int steps = squareSize / 2;\r
1689         int i;\r
1690 \r
1691         for( i=0; i<steps; i++ ) {\r
1692             BYTE r = r1 - (r1-r2) * i / steps;\r
1693             BYTE g = g1 - (g1-g2) * i / steps;\r
1694             BYTE b = b1 - (b1-b2) * i / steps;\r
1695 \r
1696             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1697             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1698             FillRect( hdc, &rc, hbrush );\r
1699             DeleteObject(hbrush);\r
1700         }\r
1701     }\r
1702     else if( mode == 2 ) {\r
1703         /* Diagonal gradient, good more or less for every piece */\r
1704         POINT triangle[3];\r
1705         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1706         HBRUSH hbrush_old;\r
1707         int steps = squareSize;\r
1708         int i;\r
1709 \r
1710         triangle[0].x = squareSize - steps;\r
1711         triangle[0].y = squareSize;\r
1712         triangle[1].x = squareSize;\r
1713         triangle[1].y = squareSize;\r
1714         triangle[2].x = squareSize;\r
1715         triangle[2].y = squareSize - steps;\r
1716 \r
1717         for( i=0; i<steps; i++ ) {\r
1718             BYTE r = r1 - (r1-r2) * i / steps;\r
1719             BYTE g = g1 - (g1-g2) * i / steps;\r
1720             BYTE b = b1 - (b1-b2) * i / steps;\r
1721 \r
1722             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1723             hbrush_old = SelectObject( hdc, hbrush );\r
1724             Polygon( hdc, triangle, 3 );\r
1725             SelectObject( hdc, hbrush_old );\r
1726             DeleteObject(hbrush);\r
1727             triangle[0].x++;\r
1728             triangle[2].y++;\r
1729         }\r
1730 \r
1731         SelectObject( hdc, hpen );\r
1732     }\r
1733 }\r
1734 \r
1735 /*\r
1736     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1737     seems to work ok. The main problem here is to find the "inside" of a chess\r
1738     piece: follow the steps as explained below.\r
1739 */\r
1740 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1741 {\r
1742     HBITMAP hbm;\r
1743     HBITMAP hbm_old;\r
1744     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1745     RECT rc;\r
1746     SIZE sz;\r
1747     POINT pt;\r
1748     int backColor = whitePieceColor; \r
1749     int foreColor = blackPieceColor;\r
1750     \r
1751     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1752         backColor = appData.fontBackColorWhite;\r
1753         foreColor = appData.fontForeColorWhite;\r
1754     }\r
1755     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1756         backColor = appData.fontBackColorBlack;\r
1757         foreColor = appData.fontForeColorBlack;\r
1758     }\r
1759 \r
1760     /* Mask */\r
1761     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1762 \r
1763     hbm_old = SelectObject( hdc, hbm );\r
1764 \r
1765     rc.left = 0;\r
1766     rc.top = 0;\r
1767     rc.right = squareSize;\r
1768     rc.bottom = squareSize;\r
1769 \r
1770     /* Step 1: background is now black */\r
1771     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1772 \r
1773     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1774 \r
1775     pt.x = (squareSize - sz.cx) / 2;\r
1776     pt.y = (squareSize - sz.cy) / 2;\r
1777 \r
1778     SetBkMode( hdc, TRANSPARENT );\r
1779     SetTextColor( hdc, chroma );\r
1780     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1781     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1782 \r
1783     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1784     /* Step 3: the area outside the piece is filled with white */\r
1785 //    FloodFill( hdc, 0, 0, chroma );\r
1786     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1787     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1788     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1789     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1790     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1791     /* \r
1792         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1793         but if the start point is not inside the piece we're lost!\r
1794         There should be a better way to do this... if we could create a region or path\r
1795         from the fill operation we would be fine for example.\r
1796     */\r
1797 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1798     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1799 \r
1800     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1801         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1802         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1803 \r
1804         SelectObject( dc2, bm2 );\r
1805         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1806         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1807         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1808         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1809         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1810 \r
1811         DeleteDC( dc2 );\r
1812         DeleteObject( bm2 );\r
1813     }\r
1814 \r
1815     SetTextColor( hdc, 0 );\r
1816     /* \r
1817         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1818         draw the piece again in black for safety.\r
1819     */\r
1820     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1821 \r
1822     SelectObject( hdc, hbm_old );\r
1823 \r
1824     if( hPieceMask[index] != NULL ) {\r
1825         DeleteObject( hPieceMask[index] );\r
1826     }\r
1827 \r
1828     hPieceMask[index] = hbm;\r
1829 \r
1830     /* Face */\r
1831     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1832 \r
1833     SelectObject( hdc, hbm );\r
1834 \r
1835     {\r
1836         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1837         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1838         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1839 \r
1840         SelectObject( dc1, hPieceMask[index] );\r
1841         SelectObject( dc2, bm2 );\r
1842         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1843         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1844         \r
1845         /* \r
1846             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1847             the piece background and deletes (makes transparent) the rest.\r
1848             Thanks to that mask, we are free to paint the background with the greates\r
1849             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1850             We use this, to make gradients and give the pieces a "roundish" look.\r
1851         */\r
1852         SetPieceBackground( hdc, backColor, 2 );\r
1853         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1854 \r
1855         DeleteDC( dc2 );\r
1856         DeleteDC( dc1 );\r
1857         DeleteObject( bm2 );\r
1858     }\r
1859 \r
1860     SetTextColor( hdc, foreColor );\r
1861     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1862 \r
1863     SelectObject( hdc, hbm_old );\r
1864 \r
1865     if( hPieceFace[index] != NULL ) {\r
1866         DeleteObject( hPieceFace[index] );\r
1867     }\r
1868 \r
1869     hPieceFace[index] = hbm;\r
1870 }\r
1871 \r
1872 static int TranslatePieceToFontPiece( int piece )\r
1873 {\r
1874     switch( piece ) {\r
1875     case BlackPawn:\r
1876         return PM_BP;\r
1877     case BlackKnight:\r
1878         return PM_BN;\r
1879     case BlackBishop:\r
1880         return PM_BB;\r
1881     case BlackRook:\r
1882         return PM_BR;\r
1883     case BlackQueen:\r
1884         return PM_BQ;\r
1885     case BlackKing:\r
1886         return PM_BK;\r
1887     case WhitePawn:\r
1888         return PM_WP;\r
1889     case WhiteKnight:\r
1890         return PM_WN;\r
1891     case WhiteBishop:\r
1892         return PM_WB;\r
1893     case WhiteRook:\r
1894         return PM_WR;\r
1895     case WhiteQueen:\r
1896         return PM_WQ;\r
1897     case WhiteKing:\r
1898         return PM_WK;\r
1899 \r
1900     case BlackAngel:\r
1901         return PM_BA;\r
1902     case BlackMarshall:\r
1903         return PM_BC;\r
1904     case BlackFerz:\r
1905         return PM_BF;\r
1906     case BlackNightrider:\r
1907         return PM_BH;\r
1908     case BlackAlfil:\r
1909         return PM_BE;\r
1910     case BlackWazir:\r
1911         return PM_BW;\r
1912     case BlackUnicorn:\r
1913         return PM_BU;\r
1914     case BlackCannon:\r
1915         return PM_BO;\r
1916     case BlackGrasshopper:\r
1917         return PM_BG;\r
1918     case BlackMan:\r
1919         return PM_BM;\r
1920     case BlackSilver:\r
1921         return PM_BSG;\r
1922     case BlackLance:\r
1923         return PM_BL;\r
1924     case BlackFalcon:\r
1925         return PM_BV;\r
1926     case BlackCobra:\r
1927         return PM_BS;\r
1928     case BlackCardinal:\r
1929         return PM_BAB;\r
1930     case BlackDragon:\r
1931         return PM_BD;\r
1932 \r
1933     case WhiteAngel:\r
1934         return PM_WA;\r
1935     case WhiteMarshall:\r
1936         return PM_WC;\r
1937     case WhiteFerz:\r
1938         return PM_WF;\r
1939     case WhiteNightrider:\r
1940         return PM_WH;\r
1941     case WhiteAlfil:\r
1942         return PM_WE;\r
1943     case WhiteWazir:\r
1944         return PM_WW;\r
1945     case WhiteUnicorn:\r
1946         return PM_WU;\r
1947     case WhiteCannon:\r
1948         return PM_WO;\r
1949     case WhiteGrasshopper:\r
1950         return PM_WG;\r
1951     case WhiteMan:\r
1952         return PM_WM;\r
1953     case WhiteSilver:\r
1954         return PM_WSG;\r
1955     case WhiteLance:\r
1956         return PM_WL;\r
1957     case WhiteFalcon:\r
1958         return PM_WV;\r
1959     case WhiteCobra:\r
1960         return PM_WS;\r
1961     case WhiteCardinal:\r
1962         return PM_WAB;\r
1963     case WhiteDragon:\r
1964         return PM_WD;\r
1965     }\r
1966 \r
1967     return 0;\r
1968 }\r
1969 \r
1970 void CreatePiecesFromFont()\r
1971 {\r
1972     LOGFONT lf;\r
1973     HDC hdc_window = NULL;\r
1974     HDC hdc = NULL;\r
1975     HFONT hfont_old;\r
1976     int fontHeight;\r
1977     int i;\r
1978 \r
1979     if( fontBitmapSquareSize < 0 ) {\r
1980         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1981         return;\r
1982     }\r
1983 \r
1984     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
1985             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1986         fontBitmapSquareSize = -1;\r
1987         return;\r
1988     }\r
1989 \r
1990     if( fontBitmapSquareSize != squareSize ) {\r
1991         hdc_window = GetDC( hwndMain );\r
1992         hdc = CreateCompatibleDC( hdc_window );\r
1993 \r
1994         if( hPieceFont != NULL ) {\r
1995             DeleteObject( hPieceFont );\r
1996         }\r
1997         else {\r
1998             for( i=0; i<=(int)BlackKing; i++ ) {\r
1999                 hPieceMask[i] = NULL;\r
2000                 hPieceFace[i] = NULL;\r
2001             }\r
2002         }\r
2003 \r
2004         fontHeight = 75;\r
2005 \r
2006         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2007             fontHeight = appData.fontPieceSize;\r
2008         }\r
2009 \r
2010         fontHeight = (fontHeight * squareSize) / 100;\r
2011 \r
2012         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2013         lf.lfWidth = 0;\r
2014         lf.lfEscapement = 0;\r
2015         lf.lfOrientation = 0;\r
2016         lf.lfWeight = FW_NORMAL;\r
2017         lf.lfItalic = 0;\r
2018         lf.lfUnderline = 0;\r
2019         lf.lfStrikeOut = 0;\r
2020         lf.lfCharSet = DEFAULT_CHARSET;\r
2021         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2022         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2023         lf.lfQuality = PROOF_QUALITY;\r
2024         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2025         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2026         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2027 \r
2028         hPieceFont = CreateFontIndirect( &lf );\r
2029 \r
2030         if( hPieceFont == NULL ) {\r
2031             fontBitmapSquareSize = -2;\r
2032         }\r
2033         else {\r
2034             /* Setup font-to-piece character table */\r
2035             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2036                 /* No (or wrong) global settings, try to detect the font */\r
2037                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2038                     /* Alpha */\r
2039                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2040                 }\r
2041                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2042                     /* DiagramTT* family */\r
2043                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2044                 }\r
2045                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2046                     /* Fairy symbols */\r
2047                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2048                 }\r
2049                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2050                     /* Good Companion (Some characters get warped as literal :-( */\r
2051                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2052                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2053                     SetCharTable(pieceToFontChar, s);\r
2054                 }\r
2055                 else {\r
2056                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2057                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2058                 }\r
2059             }\r
2060 \r
2061             /* Create bitmaps */\r
2062             hfont_old = SelectObject( hdc, hPieceFont );\r
2063             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2064                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2065                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2066 \r
2067             SelectObject( hdc, hfont_old );\r
2068 \r
2069             fontBitmapSquareSize = squareSize;\r
2070         }\r
2071     }\r
2072 \r
2073     if( hdc != NULL ) {\r
2074         DeleteDC( hdc );\r
2075     }\r
2076 \r
2077     if( hdc_window != NULL ) {\r
2078         ReleaseDC( hwndMain, hdc_window );\r
2079     }\r
2080 }\r
2081 \r
2082 HBITMAP\r
2083 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2084 {\r
2085   char name[128];\r
2086 \r
2087     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2088   if (gameInfo.event &&\r
2089       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2090       strcmp(name, "k80s") == 0) {\r
2091     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2092   }\r
2093   return LoadBitmap(hinst, name);\r
2094 }\r
2095 \r
2096 \r
2097 /* Insert a color into the program's logical palette\r
2098    structure.  This code assumes the given color is\r
2099    the result of the RGB or PALETTERGB macro, and it\r
2100    knows how those macros work (which is documented).\r
2101 */\r
2102 VOID\r
2103 InsertInPalette(COLORREF color)\r
2104 {\r
2105   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2106 \r
2107   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2108     DisplayFatalError(_("Too many colors"), 0, 1);\r
2109     pLogPal->palNumEntries--;\r
2110     return;\r
2111   }\r
2112 \r
2113   pe->peFlags = (char) 0;\r
2114   pe->peRed = (char) (0xFF & color);\r
2115   pe->peGreen = (char) (0xFF & (color >> 8));\r
2116   pe->peBlue = (char) (0xFF & (color >> 16));\r
2117   return;\r
2118 }\r
2119 \r
2120 \r
2121 VOID\r
2122 InitDrawingColors()\r
2123 {\r
2124   if (pLogPal == NULL) {\r
2125     /* Allocate enough memory for a logical palette with\r
2126      * PALETTESIZE entries and set the size and version fields\r
2127      * of the logical palette structure.\r
2128      */\r
2129     pLogPal = (NPLOGPALETTE)\r
2130       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2131                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2132     pLogPal->palVersion    = 0x300;\r
2133   }\r
2134   pLogPal->palNumEntries = 0;\r
2135 \r
2136   InsertInPalette(lightSquareColor);\r
2137   InsertInPalette(darkSquareColor);\r
2138   InsertInPalette(whitePieceColor);\r
2139   InsertInPalette(blackPieceColor);\r
2140   InsertInPalette(highlightSquareColor);\r
2141   InsertInPalette(premoveHighlightColor);\r
2142 \r
2143   /*  create a logical color palette according the information\r
2144    *  in the LOGPALETTE structure.\r
2145    */\r
2146   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2147 \r
2148   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2149   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2150   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2151   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2152   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2153   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2154   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2155   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2156   /* [AS] Force rendering of the font-based pieces */\r
2157   if( fontBitmapSquareSize > 0 ) {\r
2158     fontBitmapSquareSize = 0;\r
2159   }\r
2160 }\r
2161 \r
2162 \r
2163 int\r
2164 BoardWidth(int boardSize, int n)\r
2165 { /* [HGM] argument n added to allow different width and height */\r
2166   int lineGap = sizeInfo[boardSize].lineGap;\r
2167 \r
2168   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2169       lineGap = appData.overrideLineGap;\r
2170   }\r
2171 \r
2172   return (n + 1) * lineGap +\r
2173           n * sizeInfo[boardSize].squareSize;\r
2174 }\r
2175 \r
2176 /* Respond to board resize by dragging edge */\r
2177 VOID\r
2178 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2179 {\r
2180   BoardSize newSize = NUM_SIZES - 1;\r
2181   static int recurse = 0;\r
2182   if (IsIconic(hwndMain)) return;\r
2183   if (recurse > 0) return;\r
2184   recurse++;\r
2185   while (newSize > 0) {\r
2186         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2187         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2188            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2189     newSize--;\r
2190   } \r
2191   boardSize = newSize;\r
2192   InitDrawingSizes(boardSize, flags);\r
2193   recurse--;\r
2194 }\r
2195 \r
2196 \r
2197 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2198 \r
2199 VOID\r
2200 InitDrawingSizes(BoardSize boardSize, int flags)\r
2201 {\r
2202   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2203   ChessSquare piece;\r
2204   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2205   HDC hdc;\r
2206   SIZE clockSize, messageSize;\r
2207   HFONT oldFont;\r
2208   char buf[MSG_SIZ];\r
2209   char *str;\r
2210   HMENU hmenu = GetMenu(hwndMain);\r
2211   RECT crect, wrect, oldRect;\r
2212   int offby;\r
2213   LOGBRUSH logbrush;\r
2214 \r
2215   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2216   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2217 \r
2218   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2219   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2220 \r
2221   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2222   oldRect.top = wpMain.y;\r
2223   oldRect.right = wpMain.x + wpMain.width;\r
2224   oldRect.bottom = wpMain.y + wpMain.height;\r
2225 \r
2226   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2227   smallLayout = sizeInfo[boardSize].smallLayout;\r
2228   squareSize = sizeInfo[boardSize].squareSize;\r
2229   lineGap = sizeInfo[boardSize].lineGap;\r
2230   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2231 \r
2232   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2233       lineGap = appData.overrideLineGap;\r
2234   }\r
2235 \r
2236   if (tinyLayout != oldTinyLayout) {\r
2237     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2238     if (tinyLayout) {\r
2239       style &= ~WS_SYSMENU;\r
2240       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2241                  "&Minimize\tCtrl+F4");\r
2242     } else {\r
2243       style |= WS_SYSMENU;\r
2244       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2245     }\r
2246     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2247 \r
2248     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2249       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2250         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2251     }\r
2252     DrawMenuBar(hwndMain);\r
2253   }\r
2254 \r
2255   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2256   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2257 \r
2258   /* Get text area sizes */\r
2259   hdc = GetDC(hwndMain);\r
2260   if (appData.clockMode) {\r
2261     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2262   } else {\r
2263     snprintf(buf, MSG_SIZ, _("White"));\r
2264   }\r
2265   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2266   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2267   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2268   str = _("We only care about the height here");\r
2269   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2270   SelectObject(hdc, oldFont);\r
2271   ReleaseDC(hwndMain, hdc);\r
2272 \r
2273   /* Compute where everything goes */\r
2274   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2275         /* [HGM] logo: if either logo is on, reserve space for it */\r
2276         logoHeight =  2*clockSize.cy;\r
2277         leftLogoRect.left   = OUTER_MARGIN;\r
2278         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2279         leftLogoRect.top    = OUTER_MARGIN;\r
2280         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2281 \r
2282         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2283         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2284         rightLogoRect.top    = OUTER_MARGIN;\r
2285         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2286 \r
2287 \r
2288     whiteRect.left = leftLogoRect.right;\r
2289     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2290     whiteRect.top = OUTER_MARGIN;\r
2291     whiteRect.bottom = whiteRect.top + logoHeight;\r
2292 \r
2293     blackRect.right = rightLogoRect.left;\r
2294     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2295     blackRect.top = whiteRect.top;\r
2296     blackRect.bottom = whiteRect.bottom;\r
2297   } else {\r
2298     whiteRect.left = OUTER_MARGIN;\r
2299     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2300     whiteRect.top = OUTER_MARGIN;\r
2301     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2302 \r
2303     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2304     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2305     blackRect.top = whiteRect.top;\r
2306     blackRect.bottom = whiteRect.bottom;\r
2307 \r
2308     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2309   }\r
2310 \r
2311   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2312   if (appData.showButtonBar) {\r
2313     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2314       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2315   } else {\r
2316     messageRect.right = OUTER_MARGIN + boardWidth;\r
2317   }\r
2318   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2319   messageRect.bottom = messageRect.top + messageSize.cy;\r
2320 \r
2321   boardRect.left = OUTER_MARGIN;\r
2322   boardRect.right = boardRect.left + boardWidth;\r
2323   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2324   boardRect.bottom = boardRect.top + boardHeight;\r
2325 \r
2326   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2327   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2328   oldBoardSize = boardSize;\r
2329   oldTinyLayout = tinyLayout;\r
2330   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2331   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2332     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2333   winW *= 1 + twoBoards;\r
2334   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2335   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2336   wpMain.height = winH; //       without disturbing window attachments\r
2337   GetWindowRect(hwndMain, &wrect);\r
2338   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2339                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2340 \r
2341   // [HGM] placement: let attached windows follow size change.\r
2342   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2343   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2344   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2345   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2346   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2347 \r
2348   /* compensate if menu bar wrapped */\r
2349   GetClientRect(hwndMain, &crect);\r
2350   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2351   wpMain.height += offby;\r
2352   switch (flags) {\r
2353   case WMSZ_TOPLEFT:\r
2354     SetWindowPos(hwndMain, NULL, \r
2355                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2356                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2357     break;\r
2358 \r
2359   case WMSZ_TOPRIGHT:\r
2360   case WMSZ_TOP:\r
2361     SetWindowPos(hwndMain, NULL, \r
2362                  wrect.left, wrect.bottom - wpMain.height, \r
2363                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2364     break;\r
2365 \r
2366   case WMSZ_BOTTOMLEFT:\r
2367   case WMSZ_LEFT:\r
2368     SetWindowPos(hwndMain, NULL, \r
2369                  wrect.right - wpMain.width, wrect.top, \r
2370                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2371     break;\r
2372 \r
2373   case WMSZ_BOTTOMRIGHT:\r
2374   case WMSZ_BOTTOM:\r
2375   case WMSZ_RIGHT:\r
2376   default:\r
2377     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2378                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2379     break;\r
2380   }\r
2381 \r
2382   hwndPause = NULL;\r
2383   for (i = 0; i < N_BUTTONS; i++) {\r
2384     if (buttonDesc[i].hwnd != NULL) {\r
2385       DestroyWindow(buttonDesc[i].hwnd);\r
2386       buttonDesc[i].hwnd = NULL;\r
2387     }\r
2388     if (appData.showButtonBar) {\r
2389       buttonDesc[i].hwnd =\r
2390         CreateWindow("BUTTON", buttonDesc[i].label,\r
2391                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2392                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2393                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2394                      (HMENU) buttonDesc[i].id,\r
2395                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2396       if (tinyLayout) {\r
2397         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2398                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2399                     MAKELPARAM(FALSE, 0));\r
2400       }\r
2401       if (buttonDesc[i].id == IDM_Pause)\r
2402         hwndPause = buttonDesc[i].hwnd;\r
2403       buttonDesc[i].wndproc = (WNDPROC)\r
2404         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2405     }\r
2406   }\r
2407   if (gridPen != NULL) DeleteObject(gridPen);\r
2408   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2409   if (premovePen != NULL) DeleteObject(premovePen);\r
2410   if (lineGap != 0) {\r
2411     logbrush.lbStyle = BS_SOLID;\r
2412     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2413     gridPen =\r
2414       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2415                    lineGap, &logbrush, 0, NULL);\r
2416     logbrush.lbColor = highlightSquareColor;\r
2417     highlightPen =\r
2418       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2419                    lineGap, &logbrush, 0, NULL);\r
2420 \r
2421     logbrush.lbColor = premoveHighlightColor; \r
2422     premovePen =\r
2423       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2424                    lineGap, &logbrush, 0, NULL);\r
2425 \r
2426     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2427     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2428       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2429       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2430         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2431       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2432         BOARD_WIDTH * (squareSize + lineGap);\r
2433       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2434     }\r
2435     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2436       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2437       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2438         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2439         lineGap / 2 + (i * (squareSize + lineGap));\r
2440       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2441         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2442       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2443     }\r
2444   }\r
2445 \r
2446   /* [HGM] Licensing requirement */\r
2447 #ifdef GOTHIC\r
2448   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2449 #endif\r
2450 #ifdef FALCON\r
2451   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2452 #endif\r
2453   GothicPopUp( "", VariantNormal);\r
2454 \r
2455 \r
2456 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2457 \r
2458   /* Load piece bitmaps for this board size */\r
2459   for (i=0; i<=2; i++) {\r
2460     for (piece = WhitePawn;\r
2461          (int) piece < (int) BlackPawn;\r
2462          piece = (ChessSquare) ((int) piece + 1)) {\r
2463       if (pieceBitmap[i][piece] != NULL)\r
2464         DeleteObject(pieceBitmap[i][piece]);\r
2465     }\r
2466   }\r
2467 \r
2468   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2469   // Orthodox Chess pieces\r
2470   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2471   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2472   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2473   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2474   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2475   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2476   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2477   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2478   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2479   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2480   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2481   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2482   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2483   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2484   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2485   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2486     // in Shogi, Hijack the unused Queen for Lance\r
2487     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2488     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2489     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2490   } else {\r
2491     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2492     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2493     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2494   }\r
2495 \r
2496   if(squareSize <= 72 && squareSize >= 33) { \r
2497     /* A & C are available in most sizes now */\r
2498     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2499       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2500       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2501       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2502       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2503       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2504       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2505       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2506       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2507       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2508       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2509       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2510       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2511     } else { // Smirf-like\r
2512       if(gameInfo.variant == VariantSChess) {\r
2513         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2514         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2515         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2516       } else {\r
2517         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2518         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2519         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2520       }\r
2521     }\r
2522     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2523       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2524       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2525       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2526     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2527       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2528       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2529       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2530     } else { // WinBoard standard\r
2531       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2532       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2533       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2534     }\r
2535   }\r
2536 \r
2537 \r
2538   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2539     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2540     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2541     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2542     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2543     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2544     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2545     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2546     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2547     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2548     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2549     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2550     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2551     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2552     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2553     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2554     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2555     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2556     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2557     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2558     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2559     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2560     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2561     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2562     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2563     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2564     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2565     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2566     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2567     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2568     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2569 \r
2570     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2571       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2572       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2573       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2574       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2575       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2576       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2577       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2578       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2579       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2580       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2581       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2582       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2583     } else {\r
2584       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2585       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2586       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2587       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2588       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2589       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2590       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2591       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2592       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2593       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2594       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2595       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2596     }\r
2597 \r
2598   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2599     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2600     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2601     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2602     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2603     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2604     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2605     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2606     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2607     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2608     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2609     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2610     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2611     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2612     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2613   }\r
2614 \r
2615 \r
2616   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2617   /* special Shogi support in this size */\r
2618   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2619       for (piece = WhitePawn;\r
2620            (int) piece < (int) BlackPawn;\r
2621            piece = (ChessSquare) ((int) piece + 1)) {\r
2622         if (pieceBitmap[i][piece] != NULL)\r
2623           DeleteObject(pieceBitmap[i][piece]);\r
2624       }\r
2625     }\r
2626   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2627   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2628   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2629   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2630   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2631   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2632   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2633   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2634   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2635   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2636   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2637   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2638   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2639   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2640   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2641   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2642   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2643   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2644   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2645   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2646   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2647   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2648   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2649   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2650   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2651   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2652   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2653   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2654   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2655   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2656   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2657   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2658   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2659   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2660   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2661   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2662   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2663   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2664   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2665   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2666   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2667   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2668   minorSize = 0;\r
2669   }\r
2670 }\r
2671 \r
2672 HBITMAP\r
2673 PieceBitmap(ChessSquare p, int kind)\r
2674 {\r
2675   if ((int) p >= (int) BlackPawn)\r
2676     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2677 \r
2678   return pieceBitmap[kind][(int) p];\r
2679 }\r
2680 \r
2681 /***************************************************************/\r
2682 \r
2683 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2684 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2685 /*\r
2686 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2687 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2688 */\r
2689 \r
2690 VOID\r
2691 SquareToPos(int row, int column, int * x, int * y)\r
2692 {\r
2693   if (flipView) {\r
2694     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2695     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2696   } else {\r
2697     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2698     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2699   }\r
2700 }\r
2701 \r
2702 VOID\r
2703 DrawCoordsOnDC(HDC hdc)\r
2704 {\r
2705   static char files[] = "0123456789012345678901221098765432109876543210";\r
2706   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2707   char str[2] = { NULLCHAR, NULLCHAR };\r
2708   int oldMode, oldAlign, x, y, start, i;\r
2709   HFONT oldFont;\r
2710   HBRUSH oldBrush;\r
2711 \r
2712   if (!appData.showCoords)\r
2713     return;\r
2714 \r
2715   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2716 \r
2717   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2718   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2719   oldAlign = GetTextAlign(hdc);\r
2720   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2721 \r
2722   y = boardRect.top + lineGap;\r
2723   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2724 \r
2725   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2726   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2727     str[0] = files[start + i];\r
2728     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2729     y += squareSize + lineGap;\r
2730   }\r
2731 \r
2732   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2733 \r
2734   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2735   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2736     str[0] = ranks[start + i];\r
2737     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2738     x += squareSize + lineGap;\r
2739   }    \r
2740 \r
2741   SelectObject(hdc, oldBrush);\r
2742   SetBkMode(hdc, oldMode);\r
2743   SetTextAlign(hdc, oldAlign);\r
2744   SelectObject(hdc, oldFont);\r
2745 }\r
2746 \r
2747 VOID\r
2748 DrawGridOnDC(HDC hdc)\r
2749 {\r
2750   HPEN oldPen;\r
2751  \r
2752   if (lineGap != 0) {\r
2753     oldPen = SelectObject(hdc, gridPen);\r
2754     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2755     SelectObject(hdc, oldPen);\r
2756   }\r
2757 }\r
2758 \r
2759 #define HIGHLIGHT_PEN 0\r
2760 #define PREMOVE_PEN   1\r
2761 \r
2762 VOID\r
2763 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2764 {\r
2765   int x1, y1;\r
2766   HPEN oldPen, hPen;\r
2767   if (lineGap == 0) return;\r
2768   if (flipView) {\r
2769     x1 = boardRect.left +\r
2770       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2771     y1 = boardRect.top +\r
2772       lineGap/2 + y * (squareSize + lineGap);\r
2773   } else {\r
2774     x1 = boardRect.left +\r
2775       lineGap/2 + x * (squareSize + lineGap);\r
2776     y1 = boardRect.top +\r
2777       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2778   }\r
2779   hPen = pen ? premovePen : highlightPen;\r
2780   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2781   MoveToEx(hdc, x1, y1, NULL);\r
2782   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2783   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2784   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2785   LineTo(hdc, x1, y1);\r
2786   SelectObject(hdc, oldPen);\r
2787 }\r
2788 \r
2789 VOID\r
2790 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2791 {\r
2792   int i;\r
2793   for (i=0; i<2; i++) {\r
2794     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2795       DrawHighlightOnDC(hdc, TRUE,\r
2796                         h->sq[i].x, h->sq[i].y,\r
2797                         pen);\r
2798   }\r
2799 }\r
2800 \r
2801 /* Note: sqcolor is used only in monoMode */\r
2802 /* Note that this code is largely duplicated in woptions.c,\r
2803    function DrawSampleSquare, so that needs to be updated too */\r
2804 VOID\r
2805 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2806 {\r
2807   HBITMAP oldBitmap;\r
2808   HBRUSH oldBrush;\r
2809   int tmpSize;\r
2810 \r
2811   if (appData.blindfold) return;\r
2812 \r
2813   /* [AS] Use font-based pieces if needed */\r
2814   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2815     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2816     CreatePiecesFromFont();\r
2817 \r
2818     if( fontBitmapSquareSize == squareSize ) {\r
2819         int index = TranslatePieceToFontPiece(piece);\r
2820 \r
2821         SelectObject( tmphdc, hPieceMask[ index ] );\r
2822 \r
2823       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2824         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2825       else\r
2826         BitBlt( hdc,\r
2827             x, y,\r
2828             squareSize, squareSize,\r
2829             tmphdc,\r
2830             0, 0,\r
2831             SRCAND );\r
2832 \r
2833         SelectObject( tmphdc, hPieceFace[ index ] );\r
2834 \r
2835       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2836         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2837       else\r
2838         BitBlt( hdc,\r
2839             x, y,\r
2840             squareSize, squareSize,\r
2841             tmphdc,\r
2842             0, 0,\r
2843             SRCPAINT );\r
2844 \r
2845         return;\r
2846     }\r
2847   }\r
2848 \r
2849   if (appData.monoMode) {\r
2850     SelectObject(tmphdc, PieceBitmap(piece, \r
2851       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2852     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2853            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2854   } else {\r
2855     tmpSize = squareSize;\r
2856     if(minorSize &&\r
2857         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2858          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2859       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2860       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2861       x += (squareSize - minorSize)>>1;\r
2862       y += squareSize - minorSize - 2;\r
2863       tmpSize = minorSize;\r
2864     }\r
2865     if (color || appData.allWhite ) {\r
2866       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2867       if( color )\r
2868               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2869       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2870       if(appData.upsideDown && color==flipView)\r
2871         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2872       else\r
2873         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2874       /* Use black for outline of white pieces */\r
2875       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2876       if(appData.upsideDown && color==flipView)\r
2877         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2878       else\r
2879         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2880     } else {\r
2881       /* Use square color for details of black pieces */\r
2882       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2883       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2884       if(appData.upsideDown && !flipView)\r
2885         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2886       else\r
2887         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2888     }\r
2889     SelectObject(hdc, oldBrush);\r
2890     SelectObject(tmphdc, oldBitmap);\r
2891   }\r
2892 }\r
2893 \r
2894 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2895 int GetBackTextureMode( int algo )\r
2896 {\r
2897     int result = BACK_TEXTURE_MODE_DISABLED;\r
2898 \r
2899     switch( algo ) \r
2900     {\r
2901         case BACK_TEXTURE_MODE_PLAIN:\r
2902             result = 1; /* Always use identity map */\r
2903             break;\r
2904         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2905             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2906             break;\r
2907     }\r
2908 \r
2909     return result;\r
2910 }\r
2911 \r
2912 /* \r
2913     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2914     to handle redraws cleanly (as random numbers would always be different).\r
2915 */\r
2916 VOID RebuildTextureSquareInfo()\r
2917 {\r
2918     BITMAP bi;\r
2919     int lite_w = 0;\r
2920     int lite_h = 0;\r
2921     int dark_w = 0;\r
2922     int dark_h = 0;\r
2923     int row;\r
2924     int col;\r
2925 \r
2926     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2927 \r
2928     if( liteBackTexture != NULL ) {\r
2929         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2930             lite_w = bi.bmWidth;\r
2931             lite_h = bi.bmHeight;\r
2932         }\r
2933     }\r
2934 \r
2935     if( darkBackTexture != NULL ) {\r
2936         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2937             dark_w = bi.bmWidth;\r
2938             dark_h = bi.bmHeight;\r
2939         }\r
2940     }\r
2941 \r
2942     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2943         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2944             if( (col + row) & 1 ) {\r
2945                 /* Lite square */\r
2946                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2947                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2948                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2949                   else\r
2950                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2951                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2952                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2953                   else\r
2954                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2955                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2956                 }\r
2957             }\r
2958             else {\r
2959                 /* Dark square */\r
2960                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2961                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2962                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2963                   else\r
2964                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2965                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2966                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2967                   else\r
2968                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2969                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2970                 }\r
2971             }\r
2972         }\r
2973     }\r
2974 }\r
2975 \r
2976 /* [AS] Arrow highlighting support */\r
2977 \r
2978 static double A_WIDTH = 5; /* Width of arrow body */\r
2979 \r
2980 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2981 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2982 \r
2983 static double Sqr( double x )\r
2984 {\r
2985     return x*x;\r
2986 }\r
2987 \r
2988 static int Round( double x )\r
2989 {\r
2990     return (int) (x + 0.5);\r
2991 }\r
2992 \r
2993 /* Draw an arrow between two points using current settings */\r
2994 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2995 {\r
2996     POINT arrow[7];\r
2997     double dx, dy, j, k, x, y;\r
2998 \r
2999     if( d_x == s_x ) {\r
3000         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3001 \r
3002         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3003         arrow[0].y = s_y;\r
3004 \r
3005         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3006         arrow[1].y = d_y - h;\r
3007 \r
3008         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3009         arrow[2].y = d_y - h;\r
3010 \r
3011         arrow[3].x = d_x;\r
3012         arrow[3].y = d_y;\r
3013 \r
3014         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3015         arrow[5].y = d_y - h;\r
3016 \r
3017         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3018         arrow[4].y = d_y - h;\r
3019 \r
3020         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3021         arrow[6].y = s_y;\r
3022     }\r
3023     else if( d_y == s_y ) {\r
3024         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3025 \r
3026         arrow[0].x = s_x;\r
3027         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3028 \r
3029         arrow[1].x = d_x - w;\r
3030         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3031 \r
3032         arrow[2].x = d_x - w;\r
3033         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3034 \r
3035         arrow[3].x = d_x;\r
3036         arrow[3].y = d_y;\r
3037 \r
3038         arrow[5].x = d_x - w;\r
3039         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3040 \r
3041         arrow[4].x = d_x - w;\r
3042         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3043 \r
3044         arrow[6].x = s_x;\r
3045         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3046     }\r
3047     else {\r
3048         /* [AS] Needed a lot of paper for this! :-) */\r
3049         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3050         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3051   \r
3052         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3053 \r
3054         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3055 \r
3056         x = s_x;\r
3057         y = s_y;\r
3058 \r
3059         arrow[0].x = Round(x - j);\r
3060         arrow[0].y = Round(y + j*dx);\r
3061 \r
3062         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3063         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3064 \r
3065         if( d_x > s_x ) {\r
3066             x = (double) d_x - k;\r
3067             y = (double) d_y - k*dy;\r
3068         }\r
3069         else {\r
3070             x = (double) d_x + k;\r
3071             y = (double) d_y + k*dy;\r
3072         }\r
3073 \r
3074         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3075 \r
3076         arrow[6].x = Round(x - j);\r
3077         arrow[6].y = Round(y + j*dx);\r
3078 \r
3079         arrow[2].x = Round(arrow[6].x + 2*j);\r
3080         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3081 \r
3082         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3083         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3084 \r
3085         arrow[4].x = d_x;\r
3086         arrow[4].y = d_y;\r
3087 \r
3088         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3089         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3090     }\r
3091 \r
3092     Polygon( hdc, arrow, 7 );\r
3093 }\r
3094 \r
3095 /* [AS] Draw an arrow between two squares */\r
3096 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3097 {\r
3098     int s_x, s_y, d_x, d_y;\r
3099     HPEN hpen;\r
3100     HPEN holdpen;\r
3101     HBRUSH hbrush;\r
3102     HBRUSH holdbrush;\r
3103     LOGBRUSH stLB;\r
3104 \r
3105     if( s_col == d_col && s_row == d_row ) {\r
3106         return;\r
3107     }\r
3108 \r
3109     /* Get source and destination points */\r
3110     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3111     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3112 \r
3113     if( d_y > s_y ) {\r
3114         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3115     }\r
3116     else if( d_y < s_y ) {\r
3117         d_y += squareSize / 2 + squareSize / 4;\r
3118     }\r
3119     else {\r
3120         d_y += squareSize / 2;\r
3121     }\r
3122 \r
3123     if( d_x > s_x ) {\r
3124         d_x += squareSize / 2 - squareSize / 4;\r
3125     }\r
3126     else if( d_x < s_x ) {\r
3127         d_x += squareSize / 2 + squareSize / 4;\r
3128     }\r
3129     else {\r
3130         d_x += squareSize / 2;\r
3131     }\r
3132 \r
3133     s_x += squareSize / 2;\r
3134     s_y += squareSize / 2;\r
3135 \r
3136     /* Adjust width */\r
3137     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3138 \r
3139     /* Draw */\r
3140     stLB.lbStyle = BS_SOLID;\r
3141     stLB.lbColor = appData.highlightArrowColor;\r
3142     stLB.lbHatch = 0;\r
3143 \r
3144     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3145     holdpen = SelectObject( hdc, hpen );\r
3146     hbrush = CreateBrushIndirect( &stLB );\r
3147     holdbrush = SelectObject( hdc, hbrush );\r
3148 \r
3149     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3150 \r
3151     SelectObject( hdc, holdpen );\r
3152     SelectObject( hdc, holdbrush );\r
3153     DeleteObject( hpen );\r
3154     DeleteObject( hbrush );\r
3155 }\r
3156 \r
3157 BOOL HasHighlightInfo()\r
3158 {\r
3159     BOOL result = FALSE;\r
3160 \r
3161     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3162         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3163     {\r
3164         result = TRUE;\r
3165     }\r
3166 \r
3167     return result;\r
3168 }\r
3169 \r
3170 BOOL IsDrawArrowEnabled()\r
3171 {\r
3172     BOOL result = FALSE;\r
3173 \r
3174     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3175         result = TRUE;\r
3176     }\r
3177 \r
3178     return result;\r
3179 }\r
3180 \r
3181 VOID DrawArrowHighlight( HDC hdc )\r
3182 {\r
3183     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3184         DrawArrowBetweenSquares( hdc,\r
3185             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3186             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3187     }\r
3188 }\r
3189 \r
3190 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3191 {\r
3192     HRGN result = NULL;\r
3193 \r
3194     if( HasHighlightInfo() ) {\r
3195         int x1, y1, x2, y2;\r
3196         int sx, sy, dx, dy;\r
3197 \r
3198         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3199         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3200 \r
3201         sx = MIN( x1, x2 );\r
3202         sy = MIN( y1, y2 );\r
3203         dx = MAX( x1, x2 ) + squareSize;\r
3204         dy = MAX( y1, y2 ) + squareSize;\r
3205 \r
3206         result = CreateRectRgn( sx, sy, dx, dy );\r
3207     }\r
3208 \r
3209     return result;\r
3210 }\r
3211 \r
3212 /*\r
3213     Warning: this function modifies the behavior of several other functions. \r
3214     \r
3215     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3216     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3217     repaint is scattered all over the place, which is not good for features such as\r
3218     "arrow highlighting" that require a full repaint of the board.\r
3219 \r
3220     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3221     user interaction, when speed is not so important) but especially to avoid errors\r
3222     in the displayed graphics.\r
3223 \r
3224     In such patched places, I always try refer to this function so there is a single\r
3225     place to maintain knowledge.\r
3226     \r
3227     To restore the original behavior, just return FALSE unconditionally.\r
3228 */\r
3229 BOOL IsFullRepaintPreferrable()\r
3230 {\r
3231     BOOL result = FALSE;\r
3232 \r
3233     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3234         /* Arrow may appear on the board */\r
3235         result = TRUE;\r
3236     }\r
3237 \r
3238     return result;\r
3239 }\r
3240 \r
3241 /* \r
3242     This function is called by DrawPosition to know whether a full repaint must\r
3243     be forced or not.\r
3244 \r
3245     Only DrawPosition may directly call this function, which makes use of \r
3246     some state information. Other function should call DrawPosition specifying \r
3247     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3248 */\r
3249 BOOL DrawPositionNeedsFullRepaint()\r
3250 {\r
3251     BOOL result = FALSE;\r
3252 \r
3253     /* \r
3254         Probably a slightly better policy would be to trigger a full repaint\r
3255         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3256         but animation is fast enough that it's difficult to notice.\r
3257     */\r
3258     if( animInfo.piece == EmptySquare ) {\r
3259         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3260             result = TRUE;\r
3261         }\r
3262     }\r
3263 \r
3264     return result;\r
3265 }\r
3266 \r
3267 VOID\r
3268 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3269 {\r
3270   int row, column, x, y, square_color, piece_color;\r
3271   ChessSquare piece;\r
3272   HBRUSH oldBrush;\r
3273   HDC texture_hdc = NULL;\r
3274 \r
3275   /* [AS] Initialize background textures if needed */\r
3276   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3277       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3278       if( backTextureSquareSize != squareSize \r
3279        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3280           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3281           backTextureSquareSize = squareSize;\r
3282           RebuildTextureSquareInfo();\r
3283       }\r
3284 \r
3285       texture_hdc = CreateCompatibleDC( hdc );\r
3286   }\r
3287 \r
3288   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3289     for (column = 0; column < BOARD_WIDTH; column++) {\r
3290   \r
3291       SquareToPos(row, column, &x, &y);\r
3292 \r
3293       piece = board[row][column];\r
3294 \r
3295       square_color = ((column + row) % 2) == 1;\r
3296       if( gameInfo.variant == VariantXiangqi ) {\r
3297           square_color = !InPalace(row, column);\r
3298           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3299           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3300       }\r
3301       piece_color = (int) piece < (int) BlackPawn;\r
3302 \r
3303 \r
3304       /* [HGM] holdings file: light square or black */\r
3305       if(column == BOARD_LEFT-2) {\r
3306             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3307                 square_color = 1;\r
3308             else {\r
3309                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3310                 continue;\r
3311             }\r
3312       } else\r
3313       if(column == BOARD_RGHT + 1 ) {\r
3314             if( row < gameInfo.holdingsSize )\r
3315                 square_color = 1;\r
3316             else {\r
3317                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3318                 continue;\r
3319             }\r
3320       }\r
3321       if(column == BOARD_LEFT-1 ) /* left align */\r
3322             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3323       else if( column == BOARD_RGHT) /* right align */\r
3324             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3325       else\r
3326       if (appData.monoMode) {\r
3327         if (piece == EmptySquare) {\r
3328           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3329                  square_color ? WHITENESS : BLACKNESS);\r
3330         } else {\r
3331           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3332         }\r
3333       } \r
3334       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3335           /* [AS] Draw the square using a texture bitmap */\r
3336           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3337           int r = row, c = column; // [HGM] do not flip board in flipView\r
3338           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3339 \r
3340           DrawTile( x, y, \r
3341               squareSize, squareSize, \r
3342               hdc, \r
3343               texture_hdc,\r
3344               backTextureSquareInfo[r][c].mode,\r
3345               backTextureSquareInfo[r][c].x,\r
3346               backTextureSquareInfo[r][c].y );\r
3347 \r
3348           SelectObject( texture_hdc, hbm );\r
3349 \r
3350           if (piece != EmptySquare) {\r
3351               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3352           }\r
3353       }\r
3354       else {\r
3355         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3356 \r
3357         oldBrush = SelectObject(hdc, brush );\r
3358         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3359         SelectObject(hdc, oldBrush);\r
3360         if (piece != EmptySquare)\r
3361           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3362       }\r
3363     }\r
3364   }\r
3365 \r
3366   if( texture_hdc != NULL ) {\r
3367     DeleteDC( texture_hdc );\r
3368   }\r
3369 }\r
3370 \r
3371 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3372 void fputDW(FILE *f, int x)\r
3373 {\r
3374         fputc(x     & 255, f);\r
3375         fputc(x>>8  & 255, f);\r
3376         fputc(x>>16 & 255, f);\r
3377         fputc(x>>24 & 255, f);\r
3378 }\r
3379 \r
3380 #define MAX_CLIPS 200   /* more than enough */\r
3381 \r
3382 VOID\r
3383 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3384 {\r
3385 //  HBITMAP bufferBitmap;\r
3386   BITMAP bi;\r
3387 //  RECT Rect;\r
3388   HDC tmphdc;\r
3389   HBITMAP hbm;\r
3390   int w = 100, h = 50;\r
3391 \r
3392   if(logo == NULL) {\r
3393     if(!logoHeight) return;\r
3394     FillRect( hdc, &logoRect, whitePieceBrush );\r
3395   }\r
3396 //  GetClientRect(hwndMain, &Rect);\r
3397 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3398 //                                      Rect.bottom-Rect.top+1);\r
3399   tmphdc = CreateCompatibleDC(hdc);\r
3400   hbm = SelectObject(tmphdc, logo);\r
3401   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3402             w = bi.bmWidth;\r
3403             h = bi.bmHeight;\r
3404   }\r
3405   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3406                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3407   SelectObject(tmphdc, hbm);\r
3408   DeleteDC(tmphdc);\r
3409 }\r
3410 \r
3411 VOID\r
3412 DisplayLogos()\r
3413 {\r
3414   if(logoHeight) {\r
3415         HDC hdc = GetDC(hwndMain);\r
3416         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3417         if(appData.autoLogo) {\r
3418           \r
3419           switch(gameMode) { // pick logos based on game mode\r
3420             case IcsObserving:\r
3421                 whiteLogo = second.programLogo; // ICS logo\r
3422                 blackLogo = second.programLogo;\r
3423             default:\r
3424                 break;\r
3425             case IcsPlayingWhite:\r
3426                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3427                 blackLogo = second.programLogo; // ICS logo\r
3428                 break;\r
3429             case IcsPlayingBlack:\r
3430                 whiteLogo = second.programLogo; // ICS logo\r
3431                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3432                 break;\r
3433             case TwoMachinesPlay:\r
3434                 if(first.twoMachinesColor[0] == 'b') {\r
3435                     whiteLogo = second.programLogo;\r
3436                     blackLogo = first.programLogo;\r
3437                 }\r
3438                 break;\r
3439             case MachinePlaysWhite:\r
3440                 blackLogo = userLogo;\r
3441                 break;\r
3442             case MachinePlaysBlack:\r
3443                 whiteLogo = userLogo;\r
3444                 blackLogo = first.programLogo;\r
3445           }\r
3446         }\r
3447         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3448         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3449         ReleaseDC(hwndMain, hdc);\r
3450   }\r
3451 }\r
3452 \r
3453 void\r
3454 UpdateLogos(int display)\r
3455 { // called after loading new engine(s), in tourney or from menu\r
3456   LoadLogo(&first, 0, FALSE);\r
3457   LoadLogo(&second, 1, appData.icsActive);\r
3458   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3459   if(display) DisplayLogos();\r
3460 }\r
3461 \r
3462 static HDC hdcSeek;\r
3463 \r
3464 // [HGM] seekgraph\r
3465 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3466 {\r
3467     POINT stPt;\r
3468     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3469     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3470     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3471     SelectObject( hdcSeek, hp );\r
3472 }\r
3473 \r
3474 // front-end wrapper for drawing functions to do rectangles\r
3475 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3476 {\r
3477     HPEN hp;\r
3478     RECT rc;\r
3479 \r
3480     if (hdcSeek == NULL) {\r
3481     hdcSeek = GetDC(hwndMain);\r
3482       if (!appData.monoMode) {\r
3483         SelectPalette(hdcSeek, hPal, FALSE);\r
3484         RealizePalette(hdcSeek);\r
3485       }\r
3486     }\r
3487     hp = SelectObject( hdcSeek, gridPen );\r
3488     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3489     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3490     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3491     SelectObject( hdcSeek, hp );\r
3492 }\r
3493 \r
3494 // front-end wrapper for putting text in graph\r
3495 void DrawSeekText(char *buf, int x, int y)\r
3496 {\r
3497         SIZE stSize;\r
3498         SetBkMode( hdcSeek, TRANSPARENT );\r
3499         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3500         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3501 }\r
3502 \r
3503 void DrawSeekDot(int x, int y, int color)\r
3504 {\r
3505         int square = color & 0x80;\r
3506         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3507                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3508         color &= 0x7F;\r
3509         if(square)\r
3510             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3511                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3512         else\r
3513             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3514                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3515             SelectObject(hdcSeek, oldBrush);\r
3516 }\r
3517 \r
3518 VOID\r
3519 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3520 {\r
3521   static Board lastReq[2], lastDrawn[2];\r
3522   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3523   static int lastDrawnFlipView = 0;\r
3524   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3525   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3526   HDC tmphdc;\r
3527   HDC hdcmem;\r
3528   HBITMAP bufferBitmap;\r
3529   HBITMAP oldBitmap;\r
3530   RECT Rect;\r
3531   HRGN clips[MAX_CLIPS];\r
3532   ChessSquare dragged_piece = EmptySquare;\r
3533   int nr = twoBoards*partnerUp;\r
3534 \r
3535   /* I'm undecided on this - this function figures out whether a full\r
3536    * repaint is necessary on its own, so there's no real reason to have the\r
3537    * caller tell it that.  I think this can safely be set to FALSE - but\r
3538    * if we trust the callers not to request full repaints unnessesarily, then\r
3539    * we could skip some clipping work.  In other words, only request a full\r
3540    * redraw when the majority of pieces have changed positions (ie. flip, \r
3541    * gamestart and similar)  --Hawk\r
3542    */\r
3543   Boolean fullrepaint = repaint;\r
3544 \r
3545   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3546 \r
3547   if( DrawPositionNeedsFullRepaint() ) {\r
3548       fullrepaint = TRUE;\r
3549   }\r
3550 \r
3551   if (board == NULL) {\r
3552     if (!lastReqValid[nr]) {\r
3553       return;\r
3554     }\r
3555     board = lastReq[nr];\r
3556   } else {\r
3557     CopyBoard(lastReq[nr], board);\r
3558     lastReqValid[nr] = 1;\r
3559   }\r
3560 \r
3561   if (doingSizing) {\r
3562     return;\r
3563   }\r
3564 \r
3565   if (IsIconic(hwndMain)) {\r
3566     return;\r
3567   }\r
3568 \r
3569   if (hdc == NULL) {\r
3570     hdc = GetDC(hwndMain);\r
3571     if (!appData.monoMode) {\r
3572       SelectPalette(hdc, hPal, FALSE);\r
3573       RealizePalette(hdc);\r
3574     }\r
3575     releaseDC = TRUE;\r
3576   } else {\r
3577     releaseDC = FALSE;\r
3578   }\r
3579 \r
3580   /* Create some work-DCs */\r
3581   hdcmem = CreateCompatibleDC(hdc);\r
3582   tmphdc = CreateCompatibleDC(hdc);\r
3583 \r
3584   /* If dragging is in progress, we temporarely remove the piece */\r
3585   /* [HGM] or temporarily decrease count if stacked              */\r
3586   /*       !! Moved to before board compare !!                   */\r
3587   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3588     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3589     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3590             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3591         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3592     } else \r
3593     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3594             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3595         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3596     } else \r
3597         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3598   }\r
3599 \r
3600   /* Figure out which squares need updating by comparing the \r
3601    * newest board with the last drawn board and checking if\r
3602    * flipping has changed.\r
3603    */\r
3604   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3605     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3606       for (column = 0; column < BOARD_WIDTH; column++) {\r
3607         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3608           SquareToPos(row, column, &x, &y);\r
3609           clips[num_clips++] =\r
3610             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3611         }\r
3612       }\r
3613     }\r
3614    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3615     for (i=0; i<2; i++) {\r
3616       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3617           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3618         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3619             lastDrawnHighlight.sq[i].y >= 0) {\r
3620           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3621                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3622           clips[num_clips++] =\r
3623             CreateRectRgn(x - lineGap, y - lineGap, \r
3624                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3625         }\r
3626         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3627           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3628           clips[num_clips++] =\r
3629             CreateRectRgn(x - lineGap, y - lineGap, \r
3630                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3631         }\r
3632       }\r
3633     }\r
3634     for (i=0; i<2; i++) {\r
3635       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3636           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3637         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3638             lastDrawnPremove.sq[i].y >= 0) {\r
3639           SquareToPos(lastDrawnPremove.sq[i].y,\r
3640                       lastDrawnPremove.sq[i].x, &x, &y);\r
3641           clips[num_clips++] =\r
3642             CreateRectRgn(x - lineGap, y - lineGap, \r
3643                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3644         }\r
3645         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3646             premoveHighlightInfo.sq[i].y >= 0) {\r
3647           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3648                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3649           clips[num_clips++] =\r
3650             CreateRectRgn(x - lineGap, y - lineGap, \r
3651                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3652         }\r
3653       }\r
3654     }\r
3655    } else { // nr == 1\r
3656         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3657         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3658         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3659         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3660       for (i=0; i<2; i++) {\r
3661         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3662             partnerHighlightInfo.sq[i].y >= 0) {\r
3663           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3664                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3665           clips[num_clips++] =\r
3666             CreateRectRgn(x - lineGap, y - lineGap, \r
3667                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3668         }\r
3669         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3670             oldPartnerHighlight.sq[i].y >= 0) {\r
3671           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3672                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3673           clips[num_clips++] =\r
3674             CreateRectRgn(x - lineGap, y - lineGap, \r
3675                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3676         }\r
3677       }\r
3678    }\r
3679   } else {\r
3680     fullrepaint = TRUE;\r
3681   }\r
3682 \r
3683   /* Create a buffer bitmap - this is the actual bitmap\r
3684    * being written to.  When all the work is done, we can\r
3685    * copy it to the real DC (the screen).  This avoids\r
3686    * the problems with flickering.\r
3687    */\r
3688   GetClientRect(hwndMain, &Rect);\r
3689   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3690                                         Rect.bottom-Rect.top+1);\r
3691   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3692   if (!appData.monoMode) {\r
3693     SelectPalette(hdcmem, hPal, FALSE);\r
3694   }\r
3695 \r
3696   /* Create clips for dragging */\r
3697   if (!fullrepaint) {\r
3698     if (dragInfo.from.x >= 0) {\r
3699       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3700       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3701     }\r
3702     if (dragInfo.start.x >= 0) {\r
3703       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3704       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3705     }\r
3706     if (dragInfo.pos.x >= 0) {\r
3707       x = dragInfo.pos.x - squareSize / 2;\r
3708       y = dragInfo.pos.y - squareSize / 2;\r
3709       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3710     }\r
3711     if (dragInfo.lastpos.x >= 0) {\r
3712       x = dragInfo.lastpos.x - squareSize / 2;\r
3713       y = dragInfo.lastpos.y - squareSize / 2;\r
3714       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3715     }\r
3716   }\r
3717 \r
3718   /* Are we animating a move?  \r
3719    * If so, \r
3720    *   - remove the piece from the board (temporarely)\r
3721    *   - calculate the clipping region\r
3722    */\r
3723   if (!fullrepaint) {\r
3724     if (animInfo.piece != EmptySquare) {\r
3725       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3726       x = boardRect.left + animInfo.lastpos.x;\r
3727       y = boardRect.top + animInfo.lastpos.y;\r
3728       x2 = boardRect.left + animInfo.pos.x;\r
3729       y2 = boardRect.top + animInfo.pos.y;\r
3730       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3731       /* Slight kludge.  The real problem is that after AnimateMove is\r
3732          done, the position on the screen does not match lastDrawn.\r
3733          This currently causes trouble only on e.p. captures in\r
3734          atomic, where the piece moves to an empty square and then\r
3735          explodes.  The old and new positions both had an empty square\r
3736          at the destination, but animation has drawn a piece there and\r
3737          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3738       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3739     }\r
3740   }\r
3741 \r
3742   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3743   if (num_clips == 0)\r
3744     fullrepaint = TRUE;\r
3745 \r
3746   /* Set clipping on the memory DC */\r
3747   if (!fullrepaint) {\r
3748     SelectClipRgn(hdcmem, clips[0]);\r
3749     for (x = 1; x < num_clips; x++) {\r
3750       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3751         abort();  // this should never ever happen!\r
3752     }\r
3753   }\r
3754 \r
3755   /* Do all the drawing to the memory DC */\r
3756   if(explodeInfo.radius) { // [HGM] atomic\r
3757         HBRUSH oldBrush;\r
3758         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3759         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3760         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3761         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3762         x += squareSize/2;\r
3763         y += squareSize/2;\r
3764         if(!fullrepaint) {\r
3765           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3766           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3767         }\r
3768         DrawGridOnDC(hdcmem);\r
3769         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3770         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3771         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3772         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3773         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3774         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3775         SelectObject(hdcmem, oldBrush);\r
3776   } else {\r
3777     DrawGridOnDC(hdcmem);\r
3778     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3779         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3780         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3781     } else {\r
3782         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3783         oldPartnerHighlight = partnerHighlightInfo;\r
3784     }\r
3785     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3786   }\r
3787   if(nr == 0) // [HGM] dual: markers only on left board\r
3788   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3789     for (column = 0; column < BOARD_WIDTH; column++) {\r
3790         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3791             HBRUSH oldBrush = SelectObject(hdcmem, \r
3792                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3793             SquareToPos(row, column, &x, &y);\r
3794             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3795                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3796             SelectObject(hdcmem, oldBrush);\r
3797         }\r
3798     }\r
3799   }\r
3800 \r
3801   if( appData.highlightMoveWithArrow ) {\r
3802     DrawArrowHighlight(hdcmem);\r
3803   }\r
3804 \r
3805   DrawCoordsOnDC(hdcmem);\r
3806 \r
3807   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3808                  /* to make sure lastDrawn contains what is actually drawn */\r
3809 \r
3810   /* Put the dragged piece back into place and draw it (out of place!) */\r
3811     if (dragged_piece != EmptySquare) {\r
3812     /* [HGM] or restack */\r
3813     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3814                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3815     else\r
3816     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3817                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3818     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3819     x = dragInfo.pos.x - squareSize / 2;\r
3820     y = dragInfo.pos.y - squareSize / 2;\r
3821     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3822                   ((int) dragInfo.piece < (int) BlackPawn), \r
3823                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3824   }   \r
3825   \r
3826   /* Put the animated piece back into place and draw it */\r
3827   if (animInfo.piece != EmptySquare) {\r
3828     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3829     x = boardRect.left + animInfo.pos.x;\r
3830     y = boardRect.top + animInfo.pos.y;\r
3831     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3832                   ((int) animInfo.piece < (int) BlackPawn),\r
3833                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3834   }\r
3835 \r
3836   /* Release the bufferBitmap by selecting in the old bitmap \r
3837    * and delete the memory DC\r
3838    */\r
3839   SelectObject(hdcmem, oldBitmap);\r
3840   DeleteDC(hdcmem);\r
3841 \r
3842   /* Set clipping on the target DC */\r
3843   if (!fullrepaint) {\r
3844     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3845         RECT rect;\r
3846         GetRgnBox(clips[x], &rect);\r
3847         DeleteObject(clips[x]);\r
3848         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3849                           rect.right + wpMain.width/2, rect.bottom);\r
3850     }\r
3851     SelectClipRgn(hdc, clips[0]);\r
3852     for (x = 1; x < num_clips; x++) {\r
3853       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3854         abort();   // this should never ever happen!\r
3855     } \r
3856   }\r
3857 \r
3858   /* Copy the new bitmap onto the screen in one go.\r
3859    * This way we avoid any flickering\r
3860    */\r
3861   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3862   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3863          boardRect.right - boardRect.left,\r
3864          boardRect.bottom - boardRect.top,\r
3865          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3866   if(saveDiagFlag) { \r
3867     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3868     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3869 \r
3870     GetObject(bufferBitmap, sizeof(b), &b);\r
3871     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3872         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3873         bih.biWidth = b.bmWidth;\r
3874         bih.biHeight = b.bmHeight;\r
3875         bih.biPlanes = 1;\r
3876         bih.biBitCount = b.bmBitsPixel;\r
3877         bih.biCompression = 0;\r
3878         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3879         bih.biXPelsPerMeter = 0;\r
3880         bih.biYPelsPerMeter = 0;\r
3881         bih.biClrUsed = 0;\r
3882         bih.biClrImportant = 0;\r
3883 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3884 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3885         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3886 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3887 \r
3888         wb = b.bmWidthBytes;\r
3889         // count colors\r
3890         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3891                 int k = ((int*) pData)[i];\r
3892                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3893                 if(j >= 16) break;\r
3894                 color[j] = k;\r
3895                 if(j >= nrColors) nrColors = j+1;\r
3896         }\r
3897         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3898                 INT p = 0;\r
3899                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3900                     for(w=0; w<(wb>>2); w+=2) {\r
3901                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3902                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3903                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3904                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3905                         pData[p++] = m | j<<4;\r
3906                     }\r
3907                     while(p&3) pData[p++] = 0;\r
3908                 }\r
3909                 fac = 3;\r
3910                 wb = ((wb+31)>>5)<<2;\r
3911         }\r
3912         // write BITMAPFILEHEADER\r
3913         fprintf(diagFile, "BM");\r
3914         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3915         fputDW(diagFile, 0);\r
3916         fputDW(diagFile, 0x36 + (fac?64:0));\r
3917         // write BITMAPINFOHEADER\r
3918         fputDW(diagFile, 40);\r
3919         fputDW(diagFile, b.bmWidth);\r
3920         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3921         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3922         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3923         fputDW(diagFile, 0);\r
3924         fputDW(diagFile, 0);\r
3925         fputDW(diagFile, 0);\r
3926         fputDW(diagFile, 0);\r
3927         fputDW(diagFile, 0);\r
3928         fputDW(diagFile, 0);\r
3929         // write color table\r
3930         if(fac)\r
3931         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3932         // write bitmap data\r
3933         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3934                 fputc(pData[i], diagFile);\r
3935         free(pData);\r
3936      }\r
3937   }\r
3938 \r
3939   SelectObject(tmphdc, oldBitmap);\r
3940 \r
3941   /* Massive cleanup */\r
3942   for (x = 0; x < num_clips; x++)\r
3943     DeleteObject(clips[x]);\r
3944 \r
3945   DeleteDC(tmphdc);\r
3946   DeleteObject(bufferBitmap);\r
3947 \r
3948   if (releaseDC) \r
3949     ReleaseDC(hwndMain, hdc);\r
3950   \r
3951   if (lastDrawnFlipView != flipView && nr == 0) {\r
3952     if (flipView)\r
3953       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3954     else\r
3955       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3956   }\r
3957 \r
3958 /*  CopyBoard(lastDrawn, board);*/\r
3959   lastDrawnHighlight = highlightInfo;\r
3960   lastDrawnPremove   = premoveHighlightInfo;\r
3961   lastDrawnFlipView = flipView;\r
3962   lastDrawnValid[nr] = 1;\r
3963 }\r
3964 \r
3965 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3966 int\r
3967 SaveDiagram(f)\r
3968      FILE *f;\r
3969 {\r
3970     saveDiagFlag = 1; diagFile = f;\r
3971     HDCDrawPosition(NULL, TRUE, NULL);\r
3972     saveDiagFlag = 0;\r
3973 \r
3974     fclose(f);\r
3975     return TRUE;\r
3976 }\r
3977 \r
3978 \r
3979 /*---------------------------------------------------------------------------*\\r
3980 | CLIENT PAINT PROCEDURE\r
3981 |   This is the main event-handler for the WM_PAINT message.\r
3982 |\r
3983 \*---------------------------------------------------------------------------*/\r
3984 VOID\r
3985 PaintProc(HWND hwnd)\r
3986 {\r
3987   HDC         hdc;\r
3988   PAINTSTRUCT ps;\r
3989   HFONT       oldFont;\r
3990 \r
3991   if((hdc = BeginPaint(hwnd, &ps))) {\r
3992     if (IsIconic(hwnd)) {\r
3993       DrawIcon(hdc, 2, 2, iconCurrent);\r
3994     } else {\r
3995       if (!appData.monoMode) {\r
3996         SelectPalette(hdc, hPal, FALSE);\r
3997         RealizePalette(hdc);\r
3998       }\r
3999       HDCDrawPosition(hdc, 1, NULL);\r
4000       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4001         flipView = !flipView; partnerUp = !partnerUp;\r
4002         HDCDrawPosition(hdc, 1, NULL);\r
4003         flipView = !flipView; partnerUp = !partnerUp;\r
4004       }\r
4005       oldFont =\r
4006         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4007       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4008                  ETO_CLIPPED|ETO_OPAQUE,\r
4009                  &messageRect, messageText, strlen(messageText), NULL);\r
4010       SelectObject(hdc, oldFont);\r
4011       DisplayBothClocks();\r
4012       DisplayLogos();\r
4013     }\r
4014     EndPaint(hwnd,&ps);\r
4015   }\r
4016 \r
4017   return;\r
4018 }\r
4019 \r
4020 \r
4021 /*\r
4022  * If the user selects on a border boundary, return -1; if off the board,\r
4023  *   return -2.  Otherwise map the event coordinate to the square.\r
4024  * The offset boardRect.left or boardRect.top must already have been\r
4025  *   subtracted from x.\r
4026  */\r
4027 int EventToSquare(x, limit)\r
4028      int x, limit;\r
4029 {\r
4030   if (x <= 0)\r
4031     return -2;\r
4032   if (x < lineGap)\r
4033     return -1;\r
4034   x -= lineGap;\r
4035   if ((x % (squareSize + lineGap)) >= squareSize)\r
4036     return -1;\r
4037   x /= (squareSize + lineGap);\r
4038     if (x >= limit)\r
4039     return -2;\r
4040   return x;\r
4041 }\r
4042 \r
4043 typedef struct {\r
4044   char piece;\r
4045   int command;\r
4046   char* name;\r
4047 } DropEnable;\r
4048 \r
4049 DropEnable dropEnables[] = {\r
4050   { 'P', DP_Pawn, N_("Pawn") },\r
4051   { 'N', DP_Knight, N_("Knight") },\r
4052   { 'B', DP_Bishop, N_("Bishop") },\r
4053   { 'R', DP_Rook, N_("Rook") },\r
4054   { 'Q', DP_Queen, N_("Queen") },\r
4055 };\r
4056 \r
4057 VOID\r
4058 SetupDropMenu(HMENU hmenu)\r
4059 {\r
4060   int i, count, enable;\r
4061   char *p;\r
4062   extern char white_holding[], black_holding[];\r
4063   char item[MSG_SIZ];\r
4064 \r
4065   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4066     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4067                dropEnables[i].piece);\r
4068     count = 0;\r
4069     while (p && *p++ == dropEnables[i].piece) count++;\r
4070       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4071     enable = count > 0 || !appData.testLegality\r
4072       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4073                       && !appData.icsActive);\r
4074     ModifyMenu(hmenu, dropEnables[i].command,\r
4075                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4076                dropEnables[i].command, item);\r
4077   }\r
4078 }\r
4079 \r
4080 void DragPieceBegin(int x, int y, Boolean instantly)\r
4081 {\r
4082       dragInfo.lastpos.x = boardRect.left + x;\r
4083       dragInfo.lastpos.y = boardRect.top + y;\r
4084       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4085       dragInfo.from.x = fromX;\r
4086       dragInfo.from.y = fromY;\r
4087       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4088       dragInfo.start = dragInfo.from;\r
4089       SetCapture(hwndMain);\r
4090 }\r
4091 \r
4092 void DragPieceEnd(int x, int y)\r
4093 {\r
4094     ReleaseCapture();\r
4095     dragInfo.start.x = dragInfo.start.y = -1;\r
4096     dragInfo.from = dragInfo.start;\r
4097     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4098 }\r
4099 \r
4100 void ChangeDragPiece(ChessSquare piece)\r
4101 {\r
4102     dragInfo.piece = piece;\r
4103 }\r
4104 \r
4105 /* Event handler for mouse messages */\r
4106 VOID\r
4107 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4108 {\r
4109   int x, y, menuNr;\r
4110   POINT pt;\r
4111   static int recursive = 0;\r
4112   HMENU hmenu;\r
4113   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4114 \r
4115   if (recursive) {\r
4116     if (message == WM_MBUTTONUP) {\r
4117       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4118          to the middle button: we simulate pressing the left button too!\r
4119          */\r
4120       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4121       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4122     }\r
4123     return;\r
4124   }\r
4125   recursive++;\r
4126   \r
4127   pt.x = LOWORD(lParam);\r
4128   pt.y = HIWORD(lParam);\r
4129   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4130   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4131   if (!flipView && y >= 0) {\r
4132     y = BOARD_HEIGHT - 1 - y;\r
4133   }\r
4134   if (flipView && x >= 0) {\r
4135     x = BOARD_WIDTH - 1 - x;\r
4136   }\r
4137 \r
4138   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4139 \r
4140   switch (message) {\r
4141   case WM_LBUTTONDOWN:\r
4142       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4143         ClockClick(flipClock);\r
4144       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4145         ClockClick(!flipClock);\r
4146       }\r
4147       dragInfo.start.x = dragInfo.start.y = -1;\r
4148       dragInfo.from = dragInfo.start;\r
4149     if(fromX == -1 && frozen) { // not sure where this is for\r
4150                 fromX = fromY = -1; \r
4151       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4152       break;\r
4153     }\r
4154       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4155       DrawPosition(TRUE, NULL);\r
4156     break;\r
4157 \r
4158   case WM_LBUTTONUP:\r
4159       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4160       DrawPosition(TRUE, NULL);\r
4161     break;\r
4162 \r
4163   case WM_MOUSEMOVE:\r
4164     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4165     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4166     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4167     if ((appData.animateDragging || appData.highlightDragging)\r
4168         && (wParam & MK_LBUTTON)\r
4169         && dragInfo.from.x >= 0) \r
4170     {\r
4171       BOOL full_repaint = FALSE;\r
4172 \r
4173       if (appData.animateDragging) {\r
4174         dragInfo.pos = pt;\r
4175       }\r
4176       if (appData.highlightDragging) {\r
4177         SetHighlights(fromX, fromY, x, y);\r
4178         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4179             full_repaint = TRUE;\r
4180         }\r
4181       }\r
4182       \r
4183       DrawPosition( full_repaint, NULL);\r
4184       \r
4185       dragInfo.lastpos = dragInfo.pos;\r
4186     }\r
4187     break;\r
4188 \r
4189   case WM_MOUSEWHEEL: // [DM]\r
4190     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4191        /* Mouse Wheel is being rolled forward\r
4192         * Play moves forward\r
4193         */\r
4194        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4195                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4196        /* Mouse Wheel is being rolled backward\r
4197         * Play moves backward\r
4198         */\r
4199        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4200                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4201     }\r
4202     break;\r
4203 \r
4204   case WM_MBUTTONUP:\r
4205   case WM_RBUTTONUP:\r
4206     ReleaseCapture();\r
4207     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4208     break;\r
4209  \r
4210   case WM_MBUTTONDOWN:\r
4211   case WM_RBUTTONDOWN:\r
4212     ErrorPopDown();\r
4213     ReleaseCapture();\r
4214     fromX = fromY = -1;\r
4215     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4216     dragInfo.start.x = dragInfo.start.y = -1;\r
4217     dragInfo.from = dragInfo.start;\r
4218     dragInfo.lastpos = dragInfo.pos;\r
4219     if (appData.highlightDragging) {\r
4220       ClearHighlights();\r
4221     }\r
4222     if(y == -2) {\r
4223       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4224       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4225           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4226       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4227           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4228       }\r
4229       break;\r
4230     }\r
4231     DrawPosition(TRUE, NULL);\r
4232 \r
4233     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4234     switch (menuNr) {\r
4235     case 0:\r
4236       if (message == WM_MBUTTONDOWN) {\r
4237         buttonCount = 3;  /* even if system didn't think so */\r
4238         if (wParam & MK_SHIFT) \r
4239           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4240         else\r
4241           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4242       } else { /* message == WM_RBUTTONDOWN */\r
4243         /* Just have one menu, on the right button.  Windows users don't\r
4244            think to try the middle one, and sometimes other software steals\r
4245            it, or it doesn't really exist. */\r
4246         if(gameInfo.variant != VariantShogi)\r
4247             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4248         else\r
4249             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4250       }\r
4251       break;\r
4252     case 2:\r
4253       SetCapture(hwndMain);
4254       break;\r
4255     case 1:\r
4256       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4257       SetupDropMenu(hmenu);\r
4258       MenuPopup(hwnd, pt, hmenu, -1);\r
4259     default:\r
4260       break;\r
4261     }\r
4262     break;\r
4263   }\r
4264 \r
4265   recursive--;\r
4266 }\r
4267 \r
4268 /* Preprocess messages for buttons in main window */\r
4269 LRESULT CALLBACK\r
4270 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4271 {\r
4272   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4273   int i, dir;\r
4274 \r
4275   for (i=0; i<N_BUTTONS; i++) {\r
4276     if (buttonDesc[i].id == id) break;\r
4277   }\r
4278   if (i == N_BUTTONS) return 0;\r
4279   switch (message) {\r
4280   case WM_KEYDOWN:\r
4281     switch (wParam) {\r
4282     case VK_LEFT:\r
4283     case VK_RIGHT:\r
4284       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4285       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4286       return TRUE;\r
4287     }\r
4288     break;\r
4289   case WM_CHAR:\r
4290     switch (wParam) {\r
4291     case '\r':\r
4292       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4293       return TRUE;\r
4294     default:\r
4295       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4296         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4297         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4298         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4299         SetFocus(h);\r
4300         SendMessage(h, WM_CHAR, wParam, lParam);\r
4301         return TRUE;\r
4302       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4303         TypeInEvent((char)wParam);\r
4304       }\r
4305       break;\r
4306     }\r
4307     break;\r
4308   }\r
4309   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4310 }\r
4311 \r
4312 /* Process messages for Promotion dialog box */\r
4313 LRESULT CALLBACK\r
4314 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4315 {\r
4316   char promoChar;\r
4317 \r
4318   switch (message) {\r
4319   case WM_INITDIALOG: /* message: initialize dialog box */\r
4320     /* Center the dialog over the application window */\r
4321     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4322     Translate(hDlg, DLG_PromotionKing);\r
4323     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4324       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4325        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4326        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4327                SW_SHOW : SW_HIDE);\r
4328     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4329     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4330        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4331          PieceToChar(WhiteAngel) != '~') ||\r
4332         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4333          PieceToChar(BlackAngel) != '~')   ) ?\r
4334                SW_SHOW : SW_HIDE);\r
4335     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4336        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4337          PieceToChar(WhiteMarshall) != '~') ||\r
4338         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4339          PieceToChar(BlackMarshall) != '~')   ) ?\r
4340                SW_SHOW : SW_HIDE);\r
4341     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4342     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4343        gameInfo.variant != VariantShogi ?\r
4344                SW_SHOW : SW_HIDE);\r
4345     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4346        gameInfo.variant != VariantShogi ?\r
4347                SW_SHOW : SW_HIDE);\r
4348     if(gameInfo.variant == VariantShogi) {\r
4349         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4350         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4351         SetWindowText(hDlg, "Promote?");\r
4352     }\r
4353     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4354        gameInfo.variant == VariantSuper ?\r
4355                SW_SHOW : SW_HIDE);\r
4356     return TRUE;\r
4357 \r
4358   case WM_COMMAND: /* message: received a command */\r
4359     switch (LOWORD(wParam)) {\r
4360     case IDCANCEL:\r
4361       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4362       ClearHighlights();\r
4363       DrawPosition(FALSE, NULL);\r
4364       return TRUE;\r
4365     case PB_King:\r
4366       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4367       break;\r
4368     case PB_Queen:\r
4369       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4370       break;\r
4371     case PB_Rook:\r
4372       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4373       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4374       break;\r
4375     case PB_Bishop:\r
4376       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4377       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4378       break;\r
4379     case PB_Chancellor:\r
4380       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4381       break;\r
4382     case PB_Archbishop:\r
4383       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4384       break;\r
4385     case PB_Knight:\r
4386       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4387       break;\r
4388     default:\r
4389       return FALSE;\r
4390     }\r
4391     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4392     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4393     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4394     fromX = fromY = -1;\r
4395     if (!appData.highlightLastMove) {\r
4396       ClearHighlights();\r
4397       DrawPosition(FALSE, NULL);\r
4398     }\r
4399     return TRUE;\r
4400   }\r
4401   return FALSE;\r
4402 }\r
4403 \r
4404 /* Pop up promotion dialog */\r
4405 VOID\r
4406 PromotionPopup(HWND hwnd)\r
4407 {\r
4408   FARPROC lpProc;\r
4409 \r
4410   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4411   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4412     hwnd, (DLGPROC)lpProc);\r
4413   FreeProcInstance(lpProc);\r
4414 }\r
4415 \r
4416 void\r
4417 PromotionPopUp()\r
4418 {\r
4419   DrawPosition(TRUE, NULL);\r
4420   PromotionPopup(hwndMain);\r
4421 }\r
4422 \r
4423 /* Toggle ShowThinking */\r
4424 VOID\r
4425 ToggleShowThinking()\r
4426 {\r
4427   appData.showThinking = !appData.showThinking;\r
4428   ShowThinkingEvent();\r
4429 }\r
4430 \r
4431 VOID\r
4432 LoadGameDialog(HWND hwnd, char* title)\r
4433 {\r
4434   UINT number = 0;\r
4435   FILE *f;\r
4436   char fileTitle[MSG_SIZ];\r
4437   f = OpenFileDialog(hwnd, "rb", "",\r
4438                      appData.oldSaveStyle ? "gam" : "pgn",\r
4439                      GAME_FILT,\r
4440                      title, &number, fileTitle, NULL);\r
4441   if (f != NULL) {\r
4442     cmailMsgLoaded = FALSE;\r
4443     if (number == 0) {\r
4444       int error = GameListBuild(f);\r
4445       if (error) {\r
4446         DisplayError(_("Cannot build game list"), error);\r
4447       } else if (!ListEmpty(&gameList) &&\r
4448                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4449         GameListPopUp(f, fileTitle);\r
4450         return;\r
4451       }\r
4452       GameListDestroy();\r
4453       number = 1;\r
4454     }\r
4455     LoadGame(f, number, fileTitle, FALSE);\r
4456   }\r
4457 }\r
4458 \r
4459 int get_term_width()\r
4460 {\r
4461     HDC hdc;\r
4462     TEXTMETRIC tm;\r
4463     RECT rc;\r
4464     HFONT hfont, hold_font;\r
4465     LOGFONT lf;\r
4466     HWND hText;\r
4467 \r
4468     if (hwndConsole)\r
4469         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4470     else\r
4471         return 79;\r
4472 \r
4473     // get the text metrics\r
4474     hdc = GetDC(hText);\r
4475     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4476     if (consoleCF.dwEffects & CFE_BOLD)\r
4477         lf.lfWeight = FW_BOLD;\r
4478     if (consoleCF.dwEffects & CFE_ITALIC)\r
4479         lf.lfItalic = TRUE;\r
4480     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4481         lf.lfStrikeOut = TRUE;\r
4482     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4483         lf.lfUnderline = TRUE;\r
4484     hfont = CreateFontIndirect(&lf);\r
4485     hold_font = SelectObject(hdc, hfont);\r
4486     GetTextMetrics(hdc, &tm);\r
4487     SelectObject(hdc, hold_font);\r
4488     DeleteObject(hfont);\r
4489     ReleaseDC(hText, hdc);\r
4490 \r
4491     // get the rectangle\r
4492     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4493 \r
4494     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4495 }\r
4496 \r
4497 void UpdateICSWidth(HWND hText)\r
4498 {\r
4499     LONG old_width, new_width;\r
4500 \r
4501     new_width = get_term_width(hText, FALSE);\r
4502     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4503     if (new_width != old_width)\r
4504     {\r
4505         ics_update_width(new_width);\r
4506         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4507     }\r
4508 }\r
4509 \r
4510 VOID\r
4511 ChangedConsoleFont()\r
4512 {\r
4513   CHARFORMAT cfmt;\r
4514   CHARRANGE tmpsel, sel;\r
4515   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4516   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4517   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4518   PARAFORMAT paraf;\r
4519 \r
4520   cfmt.cbSize = sizeof(CHARFORMAT);\r
4521   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4522     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4523                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4524   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4525    * size.  This was undocumented in the version of MSVC++ that I had\r
4526    * when I wrote the code, but is apparently documented now.\r
4527    */\r
4528   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4529   cfmt.bCharSet = f->lf.lfCharSet;\r
4530   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4531   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4532   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4533   /* Why are the following seemingly needed too? */\r
4534   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4535   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4536   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4537   tmpsel.cpMin = 0;\r
4538   tmpsel.cpMax = -1; /*999999?*/\r
4539   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4540   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4541   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4542    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4543    */\r
4544   paraf.cbSize = sizeof(paraf);\r
4545   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4546   paraf.dxStartIndent = 0;\r
4547   paraf.dxOffset = WRAP_INDENT;\r
4548   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4549   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4550   UpdateICSWidth(hText);\r
4551 }\r
4552 \r
4553 /*---------------------------------------------------------------------------*\\r
4554  *\r
4555  * Window Proc for main window\r
4556  *\r
4557 \*---------------------------------------------------------------------------*/\r
4558 \r
4559 /* Process messages for main window, etc. */\r
4560 LRESULT CALLBACK\r
4561 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4562 {\r
4563   FARPROC lpProc;\r
4564   int wmId, wmEvent;\r
4565   char *defName;\r
4566   FILE *f;\r
4567   UINT number;\r
4568   char fileTitle[MSG_SIZ];\r
4569   char buf[MSG_SIZ];\r
4570   static SnapData sd;\r
4571 \r
4572   switch (message) {\r
4573 \r
4574   case WM_PAINT: /* message: repaint portion of window */\r
4575     PaintProc(hwnd);\r
4576     break;\r
4577 \r
4578   case WM_ERASEBKGND:\r
4579     if (IsIconic(hwnd)) {\r
4580       /* Cheat; change the message */\r
4581       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4582     } else {\r
4583       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4584     }\r
4585     break;\r
4586 \r
4587   case WM_LBUTTONDOWN:\r
4588   case WM_MBUTTONDOWN:\r
4589   case WM_RBUTTONDOWN:\r
4590   case WM_LBUTTONUP:\r
4591   case WM_MBUTTONUP:\r
4592   case WM_RBUTTONUP:\r
4593   case WM_MOUSEMOVE:\r
4594   case WM_MOUSEWHEEL:\r
4595     MouseEvent(hwnd, message, wParam, lParam);\r
4596     break;\r
4597 \r
4598   JAWS_KB_NAVIGATION\r
4599 \r
4600   case WM_CHAR:\r
4601     \r
4602     JAWS_ALT_INTERCEPT\r
4603 \r
4604     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4605         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4606         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4607         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4608         SetFocus(h);\r
4609         SendMessage(h, message, wParam, lParam);\r
4610     } else if(lParam != KF_REPEAT) {\r
4611         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4612                 TypeInEvent((char)wParam);\r
4613         } else if((char)wParam == 003) CopyGameToClipboard();\r
4614          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4615     }\r
4616 \r
4617     break;\r
4618 \r
4619   case WM_PALETTECHANGED:\r
4620     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4621       int nnew;\r
4622       HDC hdc = GetDC(hwndMain);\r
4623       SelectPalette(hdc, hPal, TRUE);\r
4624       nnew = RealizePalette(hdc);\r
4625       if (nnew > 0) {\r
4626         paletteChanged = TRUE;\r
4627         InvalidateRect(hwnd, &boardRect, FALSE);\r
4628       }\r
4629       ReleaseDC(hwnd, hdc);\r
4630     }\r
4631     break;\r
4632 \r
4633   case WM_QUERYNEWPALETTE:\r
4634     if (!appData.monoMode /*&& paletteChanged*/) {\r
4635       int nnew;\r
4636       HDC hdc = GetDC(hwndMain);\r
4637       paletteChanged = FALSE;\r
4638       SelectPalette(hdc, hPal, FALSE);\r
4639       nnew = RealizePalette(hdc);\r
4640       if (nnew > 0) {\r
4641         InvalidateRect(hwnd, &boardRect, FALSE);\r
4642       }\r
4643       ReleaseDC(hwnd, hdc);\r
4644       return TRUE;\r
4645     }\r
4646     return FALSE;\r
4647 \r
4648   case WM_COMMAND: /* message: command from application menu */\r
4649     wmId    = LOWORD(wParam);\r
4650     wmEvent = HIWORD(wParam);\r
4651 \r
4652     switch (wmId) {\r
4653     case IDM_NewGame:\r
4654       ResetGameEvent();\r
4655       SAY("new game enter a move to play against the computer with white");\r
4656       break;\r
4657 \r
4658     case IDM_NewGameFRC:\r
4659       if( NewGameFRC() == 0 ) {\r
4660         ResetGameEvent();\r
4661       }\r
4662       break;\r
4663 \r
4664     case IDM_NewVariant:\r
4665       NewVariantPopup(hwnd);\r
4666       break;\r
4667 \r
4668     case IDM_LoadGame:\r
4669       LoadGameDialog(hwnd, _("Load Game from File"));\r
4670       break;\r
4671 \r
4672     case IDM_LoadNextGame:\r
4673       ReloadGame(1);\r
4674       break;\r
4675 \r
4676     case IDM_LoadPrevGame:\r
4677       ReloadGame(-1);\r
4678       break;\r
4679 \r
4680     case IDM_ReloadGame:\r
4681       ReloadGame(0);\r
4682       break;\r
4683 \r
4684     case IDM_LoadPosition:\r
4685       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4686         Reset(FALSE, TRUE);\r
4687       }\r
4688       number = 1;\r
4689       f = OpenFileDialog(hwnd, "rb", "",\r
4690                          appData.oldSaveStyle ? "pos" : "fen",\r
4691                          POSITION_FILT,\r
4692                          _("Load Position from File"), &number, fileTitle, NULL);\r
4693       if (f != NULL) {\r
4694         LoadPosition(f, number, fileTitle);\r
4695       }\r
4696       break;\r
4697 \r
4698     case IDM_LoadNextPosition:\r
4699       ReloadPosition(1);\r
4700       break;\r
4701 \r
4702     case IDM_LoadPrevPosition:\r
4703       ReloadPosition(-1);\r
4704       break;\r
4705 \r
4706     case IDM_ReloadPosition:\r
4707       ReloadPosition(0);\r
4708       break;\r
4709 \r
4710     case IDM_SaveGame:\r
4711       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4712       f = OpenFileDialog(hwnd, "a", defName,\r
4713                          appData.oldSaveStyle ? "gam" : "pgn",\r
4714                          GAME_FILT,\r
4715                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4716       if (f != NULL) {\r
4717         SaveGame(f, 0, "");\r
4718       }\r
4719       break;\r
4720 \r
4721     case IDM_SavePosition:\r
4722       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4723       f = OpenFileDialog(hwnd, "a", defName,\r
4724                          appData.oldSaveStyle ? "pos" : "fen",\r
4725                          POSITION_FILT,\r
4726                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4727       if (f != NULL) {\r
4728         SavePosition(f, 0, "");\r
4729       }\r
4730       break;\r
4731 \r
4732     case IDM_SaveDiagram:\r
4733       defName = "diagram";\r
4734       f = OpenFileDialog(hwnd, "wb", defName,\r
4735                          "bmp",\r
4736                          DIAGRAM_FILT,\r
4737                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4738       if (f != NULL) {\r
4739         SaveDiagram(f);\r
4740       }\r
4741       break;\r
4742 \r
4743     case IDM_CopyGame:\r
4744       CopyGameToClipboard();\r
4745       break;\r
4746 \r
4747     case IDM_PasteGame:\r
4748       PasteGameFromClipboard();\r
4749       break;\r
4750 \r
4751     case IDM_CopyGameListToClipboard:\r
4752       CopyGameListToClipboard();\r
4753       break;\r
4754 \r
4755     /* [AS] Autodetect FEN or PGN data */\r
4756     case IDM_PasteAny:\r
4757       PasteGameOrFENFromClipboard();\r
4758       break;\r
4759 \r
4760     /* [AS] Move history */\r
4761     case IDM_ShowMoveHistory:\r
4762         if( MoveHistoryIsUp() ) {\r
4763             MoveHistoryPopDown();\r
4764         }\r
4765         else {\r
4766             MoveHistoryPopUp();\r
4767         }\r
4768         break;\r
4769 \r
4770     /* [AS] Eval graph */\r
4771     case IDM_ShowEvalGraph:\r
4772         if( EvalGraphIsUp() ) {\r
4773             EvalGraphPopDown();\r
4774         }\r
4775         else {\r
4776             EvalGraphPopUp();\r
4777             SetFocus(hwndMain);\r
4778         }\r
4779         break;\r
4780 \r
4781     /* [AS] Engine output */\r
4782     case IDM_ShowEngineOutput:\r
4783         if( EngineOutputIsUp() ) {\r
4784             EngineOutputPopDown();\r
4785         }\r
4786         else {\r
4787             EngineOutputPopUp();\r
4788         }\r
4789         break;\r
4790 \r
4791     /* [AS] User adjudication */\r
4792     case IDM_UserAdjudication_White:\r
4793         UserAdjudicationEvent( +1 );\r
4794         break;\r
4795 \r
4796     case IDM_UserAdjudication_Black:\r
4797         UserAdjudicationEvent( -1 );\r
4798         break;\r
4799 \r
4800     case IDM_UserAdjudication_Draw:\r
4801         UserAdjudicationEvent( 0 );\r
4802         break;\r
4803 \r
4804     /* [AS] Game list options dialog */\r
4805     case IDM_GameListOptions:\r
4806       GameListOptions();\r
4807       break;\r
4808 \r
4809     case IDM_NewChat:\r
4810       ChatPopUp(NULL);\r
4811       break;\r
4812 \r
4813     case IDM_CopyPosition:\r
4814       CopyFENToClipboard();\r
4815       break;\r
4816 \r
4817     case IDM_PastePosition:\r
4818       PasteFENFromClipboard();\r
4819       break;\r
4820 \r
4821     case IDM_MailMove:\r
4822       MailMoveEvent();\r
4823       break;\r
4824 \r
4825     case IDM_ReloadCMailMsg:\r
4826       Reset(TRUE, TRUE);\r
4827       ReloadCmailMsgEvent(FALSE);\r
4828       break;\r
4829 \r
4830     case IDM_Minimize:\r
4831       ShowWindow(hwnd, SW_MINIMIZE);\r
4832       break;\r
4833 \r
4834     case IDM_Exit:\r
4835       ExitEvent(0);\r
4836       break;\r
4837 \r
4838     case IDM_MachineWhite:\r
4839       MachineWhiteEvent();\r
4840       /*\r
4841        * refresh the tags dialog only if it's visible\r
4842        */\r
4843       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4844           char *tags;\r
4845           tags = PGNTags(&gameInfo);\r
4846           TagsPopUp(tags, CmailMsg());\r
4847           free(tags);\r
4848       }\r
4849       SAY("computer starts playing white");\r
4850       break;\r
4851 \r
4852     case IDM_MachineBlack:\r
4853       MachineBlackEvent();\r
4854       /*\r
4855        * refresh the tags dialog only if it's visible\r
4856        */\r
4857       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4858           char *tags;\r
4859           tags = PGNTags(&gameInfo);\r
4860           TagsPopUp(tags, CmailMsg());\r
4861           free(tags);\r
4862       }\r
4863       SAY("computer starts playing black");\r
4864       break;\r
4865 \r
4866     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4867       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4868       break;\r
4869 \r
4870     case IDM_TwoMachines:\r
4871       TwoMachinesEvent();\r
4872       /*\r
4873        * refresh the tags dialog only if it's visible\r
4874        */\r
4875       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4876           char *tags;\r
4877           tags = PGNTags(&gameInfo);\r
4878           TagsPopUp(tags, CmailMsg());\r
4879           free(tags);\r
4880       }\r
4881       SAY("computer starts playing both sides");\r
4882       break;\r
4883 \r
4884     case IDM_AnalysisMode:\r
4885       if (!first.analysisSupport) {\r
4886         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4887         DisplayError(buf, 0);\r
4888       } else {\r
4889         SAY("analyzing current position");\r
4890         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4891         if (appData.icsActive) {\r
4892                if (gameMode != IcsObserving) {\r
4893                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4894                        DisplayError(buf, 0);\r
4895                        /* secure check */\r
4896                        if (appData.icsEngineAnalyze) {\r
4897                                if (appData.debugMode) \r
4898                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4899                                ExitAnalyzeMode();\r
4900                                ModeHighlight();\r
4901                                break;\r
4902                        }\r
4903                        break;\r
4904                } else {\r
4905                        /* if enable, user want disable icsEngineAnalyze */\r
4906                        if (appData.icsEngineAnalyze) {\r
4907                                ExitAnalyzeMode();\r
4908                                ModeHighlight();\r
4909                                break;\r
4910                        }\r
4911                        appData.icsEngineAnalyze = TRUE;\r
4912                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4913                }\r
4914         } \r
4915         if (!appData.showThinking) ToggleShowThinking();\r
4916         AnalyzeModeEvent();\r
4917       }\r
4918       break;\r
4919 \r
4920     case IDM_AnalyzeFile:\r
4921       if (!first.analysisSupport) {\r
4922         char buf[MSG_SIZ];\r
4923           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4924         DisplayError(buf, 0);\r
4925       } else {\r
4926         if (!appData.showThinking) ToggleShowThinking();\r
4927         AnalyzeFileEvent();\r
4928 //      LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4929         AnalysisPeriodicEvent(1);\r
4930       }\r
4931       break;\r
4932 \r
4933     case IDM_IcsClient:\r
4934       IcsClientEvent();\r
4935       break;\r
4936 \r
4937     case IDM_EditGame:\r
4938     case IDM_EditGame2:\r
4939       EditGameEvent();\r
4940       SAY("edit game");\r
4941       break;\r
4942 \r
4943     case IDM_EditPosition:\r
4944     case IDM_EditPosition2:\r
4945       EditPositionEvent();\r
4946       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4947       break;\r
4948 \r
4949     case IDM_Training:\r
4950       TrainingEvent();\r
4951       break;\r
4952 \r
4953     case IDM_ShowGameList:\r
4954       ShowGameListProc();\r
4955       break;\r
4956 \r
4957     case IDM_EditProgs1:\r
4958       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4959       break;\r
4960 \r
4961     case IDM_EditProgs2:\r
4962      LoadEnginePopUp(hwndMain);\r
4963 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4964       break;\r
4965 \r
4966     case IDM_EditServers:\r
4967       EditTagsPopUp(icsNames, &icsNames);\r
4968       break;\r
4969 \r
4970     case IDM_EditTags:\r
4971     case IDM_Tags:\r
4972       EditTagsProc();\r
4973       break;\r
4974 \r
4975     case IDM_EditBook:\r
4976       EditBookEvent();\r
4977       break;\r
4978 \r
4979     case IDM_EditComment:\r
4980     case IDM_Comment:\r
4981       if (commentUp && editComment) {\r
4982         CommentPopDown();\r
4983       } else {\r
4984         EditCommentEvent();\r
4985       }\r
4986       break;\r
4987 \r
4988     case IDM_Pause:\r
4989       PauseEvent();\r
4990       break;\r
4991 \r
4992     case IDM_Accept:\r
4993       AcceptEvent();\r
4994       break;\r
4995 \r
4996     case IDM_Decline:\r
4997       DeclineEvent();\r
4998       break;\r
4999 \r
5000     case IDM_Rematch:\r
5001       RematchEvent();\r
5002       break;\r
5003 \r
5004     case IDM_CallFlag:\r
5005       CallFlagEvent();\r
5006       break;\r
5007 \r
5008     case IDM_Draw:\r
5009       DrawEvent();\r
5010       break;\r
5011 \r
5012     case IDM_Adjourn:\r
5013       AdjournEvent();\r
5014       break;\r
5015 \r
5016     case IDM_Abort:\r
5017       AbortEvent();\r
5018       break;\r
5019 \r
5020     case IDM_Resign:\r
5021       ResignEvent();\r
5022       break;\r
5023 \r
5024     case IDM_StopObserving:\r
5025       StopObservingEvent();\r
5026       break;\r
5027 \r
5028     case IDM_StopExamining:\r
5029       StopExaminingEvent();\r
5030       break;\r
5031 \r
5032     case IDM_Upload:\r
5033       UploadGameEvent();\r
5034       break;\r
5035 \r
5036     case IDM_TypeInMove:\r
5037       TypeInEvent('\000');\r
5038       break;\r
5039 \r
5040     case IDM_TypeInName:\r
5041       PopUpNameDialog('\000');\r
5042       break;\r
5043 \r
5044     case IDM_Backward:\r
5045       BackwardEvent();\r
5046       SetFocus(hwndMain);\r
5047       break;\r
5048 \r
5049     JAWS_MENU_ITEMS\r
5050 \r
5051     case IDM_Forward:\r
5052       ForwardEvent();\r
5053       SetFocus(hwndMain);\r
5054       break;\r
5055 \r
5056     case IDM_ToStart:\r
5057       ToStartEvent();\r
5058       SetFocus(hwndMain);\r
5059       break;\r
5060 \r
5061     case IDM_ToEnd:\r
5062       ToEndEvent();\r
5063       SetFocus(hwndMain);\r
5064       break;\r
5065 \r
5066     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5067     case OPT_GameListPrev:\r
5068       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5069       break;\r
5070 \r
5071     case IDM_Revert:\r
5072       RevertEvent(FALSE);\r
5073       break;\r
5074 \r
5075     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5076       RevertEvent(TRUE);\r
5077       break;\r
5078 \r
5079     case IDM_TruncateGame:\r
5080       TruncateGameEvent();\r
5081       break;\r
5082 \r
5083     case IDM_MoveNow:\r
5084       MoveNowEvent();\r
5085       break;\r
5086 \r
5087     case IDM_RetractMove:\r
5088       RetractMoveEvent();\r
5089       break;\r
5090 \r
5091     case IDM_FlipView:\r
5092       flipView = !flipView;\r
5093       DrawPosition(FALSE, NULL);\r
5094       break;\r
5095 \r
5096     case IDM_FlipClock:\r
5097       flipClock = !flipClock;\r
5098       DisplayBothClocks();\r
5099       DisplayLogos();\r
5100       break;\r
5101 \r
5102     case IDM_MuteSounds:\r
5103       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5104       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5105                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5106       break;\r
5107 \r
5108     case IDM_GeneralOptions:\r
5109       GeneralOptionsPopup(hwnd);\r
5110       DrawPosition(TRUE, NULL);\r
5111       break;\r
5112 \r
5113     case IDM_BoardOptions:\r
5114       BoardOptionsPopup(hwnd);\r
5115       break;\r
5116 \r
5117     case IDM_EnginePlayOptions:\r
5118       EnginePlayOptionsPopup(hwnd);\r
5119       break;\r
5120 \r
5121     case IDM_Engine1Options:\r
5122       EngineOptionsPopup(hwnd, &first);\r
5123       break;\r
5124 \r
5125     case IDM_Engine2Options:\r
5126       savedHwnd = hwnd;\r
5127       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5128       EngineOptionsPopup(hwnd, &second);\r
5129       break;\r
5130 \r
5131     case IDM_OptionsUCI:\r
5132       UciOptionsPopup(hwnd);\r
5133       break;\r
5134 \r
5135     case IDM_Tourney:\r
5136       TourneyPopup(hwnd);\r
5137       break;\r
5138 \r
5139     case IDM_IcsOptions:\r
5140       IcsOptionsPopup(hwnd);\r
5141       break;\r
5142 \r
5143     case IDM_Fonts:\r
5144       FontsOptionsPopup(hwnd);\r
5145       break;\r
5146 \r
5147     case IDM_Sounds:\r
5148       SoundOptionsPopup(hwnd);\r
5149       break;\r
5150 \r
5151     case IDM_CommPort:\r
5152       CommPortOptionsPopup(hwnd);\r
5153       break;\r
5154 \r
5155     case IDM_LoadOptions:\r
5156       LoadOptionsPopup(hwnd);\r
5157       break;\r
5158 \r
5159     case IDM_SaveOptions:\r
5160       SaveOptionsPopup(hwnd);\r
5161       break;\r
5162 \r
5163     case IDM_TimeControl:\r
5164       TimeControlOptionsPopup(hwnd);\r
5165       break;\r
5166 \r
5167     case IDM_SaveSettings:\r
5168       SaveSettings(settingsFileName);\r
5169       break;\r
5170 \r
5171     case IDM_SaveSettingsOnExit:\r
5172       saveSettingsOnExit = !saveSettingsOnExit;\r
5173       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5174                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5175                                          MF_CHECKED : MF_UNCHECKED));\r
5176       break;\r
5177 \r
5178     case IDM_Hint:\r
5179       HintEvent();\r
5180       break;\r
5181 \r
5182     case IDM_Book:\r
5183       BookEvent();\r
5184       break;\r
5185 \r
5186     case IDM_AboutGame:\r
5187       AboutGameEvent();\r
5188       break;\r
5189 \r
5190     case IDM_Debug:\r
5191       appData.debugMode = !appData.debugMode;\r
5192       if (appData.debugMode) {\r
5193         char dir[MSG_SIZ];\r
5194         GetCurrentDirectory(MSG_SIZ, dir);\r
5195         SetCurrentDirectory(installDir);\r
5196         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5197         SetCurrentDirectory(dir);\r
5198         setbuf(debugFP, NULL);\r
5199       } else {\r
5200         fclose(debugFP);\r
5201         debugFP = NULL;\r
5202       }\r
5203       break;\r
5204 \r
5205     case IDM_HELPCONTENTS:\r
5206       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5207           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5208           MessageBox (GetFocus(),\r
5209                     _("Unable to activate help"),\r
5210                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5211       }\r
5212       break;\r
5213 \r
5214     case IDM_HELPSEARCH:\r
5215         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5216             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5217         MessageBox (GetFocus(),\r
5218                     _("Unable to activate help"),\r
5219                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5220       }\r
5221       break;\r
5222 \r
5223     case IDM_HELPHELP:\r
5224       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5225         MessageBox (GetFocus(),\r
5226                     _("Unable to activate help"),\r
5227                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5228       }\r
5229       break;\r
5230 \r
5231     case IDM_ABOUT:\r
5232       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5233       DialogBox(hInst, \r
5234         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5235         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5236       FreeProcInstance(lpProc);\r
5237       break;\r
5238 \r
5239     case IDM_DirectCommand1:\r
5240       AskQuestionEvent(_("Direct Command"),\r
5241                        _("Send to chess program:"), "", "1");\r
5242       break;\r
5243     case IDM_DirectCommand2:\r
5244       AskQuestionEvent(_("Direct Command"),\r
5245                        _("Send to second chess program:"), "", "2");\r
5246       break;\r
5247 \r
5248     case EP_WhitePawn:\r
5249       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5250       fromX = fromY = -1;\r
5251       break;\r
5252 \r
5253     case EP_WhiteKnight:\r
5254       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5255       fromX = fromY = -1;\r
5256       break;\r
5257 \r
5258     case EP_WhiteBishop:\r
5259       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5260       fromX = fromY = -1;\r
5261       break;\r
5262 \r
5263     case EP_WhiteRook:\r
5264       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5265       fromX = fromY = -1;\r
5266       break;\r
5267 \r
5268     case EP_WhiteQueen:\r
5269       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5270       fromX = fromY = -1;\r
5271       break;\r
5272 \r
5273     case EP_WhiteFerz:\r
5274       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5275       fromX = fromY = -1;\r
5276       break;\r
5277 \r
5278     case EP_WhiteWazir:\r
5279       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5280       fromX = fromY = -1;\r
5281       break;\r
5282 \r
5283     case EP_WhiteAlfil:\r
5284       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5285       fromX = fromY = -1;\r
5286       break;\r
5287 \r
5288     case EP_WhiteCannon:\r
5289       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5290       fromX = fromY = -1;\r
5291       break;\r
5292 \r
5293     case EP_WhiteCardinal:\r
5294       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5295       fromX = fromY = -1;\r
5296       break;\r
5297 \r
5298     case EP_WhiteMarshall:\r
5299       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5300       fromX = fromY = -1;\r
5301       break;\r
5302 \r
5303     case EP_WhiteKing:\r
5304       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5305       fromX = fromY = -1;\r
5306       break;\r
5307 \r
5308     case EP_BlackPawn:\r
5309       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5310       fromX = fromY = -1;\r
5311       break;\r
5312 \r
5313     case EP_BlackKnight:\r
5314       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5315       fromX = fromY = -1;\r
5316       break;\r
5317 \r
5318     case EP_BlackBishop:\r
5319       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5320       fromX = fromY = -1;\r
5321       break;\r
5322 \r
5323     case EP_BlackRook:\r
5324       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5325       fromX = fromY = -1;\r
5326       break;\r
5327 \r
5328     case EP_BlackQueen:\r
5329       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5330       fromX = fromY = -1;\r
5331       break;\r
5332 \r
5333     case EP_BlackFerz:\r
5334       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5335       fromX = fromY = -1;\r
5336       break;\r
5337 \r
5338     case EP_BlackWazir:\r
5339       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5340       fromX = fromY = -1;\r
5341       break;\r
5342 \r
5343     case EP_BlackAlfil:\r
5344       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5345       fromX = fromY = -1;\r
5346       break;\r
5347 \r
5348     case EP_BlackCannon:\r
5349       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5350       fromX = fromY = -1;\r
5351       break;\r
5352 \r
5353     case EP_BlackCardinal:\r
5354       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5355       fromX = fromY = -1;\r
5356       break;\r
5357 \r
5358     case EP_BlackMarshall:\r
5359       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5360       fromX = fromY = -1;\r
5361       break;\r
5362 \r
5363     case EP_BlackKing:\r
5364       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5365       fromX = fromY = -1;\r
5366       break;\r
5367 \r
5368     case EP_EmptySquare:\r
5369       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5370       fromX = fromY = -1;\r
5371       break;\r
5372 \r
5373     case EP_ClearBoard:\r
5374       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5375       fromX = fromY = -1;\r
5376       break;\r
5377 \r
5378     case EP_White:\r
5379       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5380       fromX = fromY = -1;\r
5381       break;\r
5382 \r
5383     case EP_Black:\r
5384       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5385       fromX = fromY = -1;\r
5386       break;\r
5387 \r
5388     case EP_Promote:\r
5389       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5390       fromX = fromY = -1;\r
5391       break;\r
5392 \r
5393     case EP_Demote:\r
5394       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5395       fromX = fromY = -1;\r
5396       break;\r
5397 \r
5398     case DP_Pawn:\r
5399       DropMenuEvent(WhitePawn, fromX, fromY);\r
5400       fromX = fromY = -1;\r
5401       break;\r
5402 \r
5403     case DP_Knight:\r
5404       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5405       fromX = fromY = -1;\r
5406       break;\r
5407 \r
5408     case DP_Bishop:\r
5409       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5410       fromX = fromY = -1;\r
5411       break;\r
5412 \r
5413     case DP_Rook:\r
5414       DropMenuEvent(WhiteRook, fromX, fromY);\r
5415       fromX = fromY = -1;\r
5416       break;\r
5417 \r
5418     case DP_Queen:\r
5419       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5420       fromX = fromY = -1;\r
5421       break;\r
5422 \r
5423     case IDM_English:\r
5424       barbaric = 0; appData.language = "";\r
5425       TranslateMenus(0);\r
5426       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5427       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5428       lastChecked = wmId;\r
5429       break;\r
5430 \r
5431     default:\r
5432       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5433           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5434           TranslateMenus(0);\r
5435           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5436           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5437           lastChecked = wmId;\r
5438           break;\r
5439       }\r
5440       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5441     }\r
5442     break;\r
5443 \r
5444   case WM_TIMER:\r
5445     switch (wParam) {\r
5446     case CLOCK_TIMER_ID:\r
5447       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5448       clockTimerEvent = 0;\r
5449       DecrementClocks(); /* call into back end */\r
5450       break;\r
5451     case LOAD_GAME_TIMER_ID:\r
5452       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5453       loadGameTimerEvent = 0;\r
5454       AutoPlayGameLoop(); /* call into back end */\r
5455       break;\r
5456     case ANALYSIS_TIMER_ID:\r
5457       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5458                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5459         AnalysisPeriodicEvent(0);\r
5460       } else {\r
5461         KillTimer(hwnd, analysisTimerEvent);\r
5462         analysisTimerEvent = 0;\r
5463       }\r
5464       break;\r
5465     case DELAYED_TIMER_ID:\r
5466       KillTimer(hwnd, delayedTimerEvent);\r
5467       delayedTimerEvent = 0;\r
5468       delayedTimerCallback();\r
5469       break;\r
5470     }\r
5471     break;\r
5472 \r
5473   case WM_USER_Input:\r
5474     InputEvent(hwnd, message, wParam, lParam);\r
5475     break;\r
5476 \r
5477   /* [AS] Also move "attached" child windows */\r
5478   case WM_WINDOWPOSCHANGING:\r
5479 \r
5480     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5481         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5482 \r
5483         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5484             /* Window is moving */\r
5485             RECT rcMain;\r
5486 \r
5487 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5488             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5489             rcMain.right  = wpMain.x + wpMain.width;\r
5490             rcMain.top    = wpMain.y;\r
5491             rcMain.bottom = wpMain.y + wpMain.height;\r
5492             \r
5493             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5494             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5495             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5496             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5497             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5498             wpMain.x = lpwp->x;\r
5499             wpMain.y = lpwp->y;\r
5500         }\r
5501     }\r
5502     break;\r
5503 \r
5504   /* [AS] Snapping */\r
5505   case WM_ENTERSIZEMOVE:\r
5506     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5507     if (hwnd == hwndMain) {\r
5508       doingSizing = TRUE;\r
5509       lastSizing = 0;\r
5510     }\r
5511     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5512     break;\r
5513 \r
5514   case WM_SIZING:\r
5515     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5516     if (hwnd == hwndMain) {\r
5517       lastSizing = wParam;\r
5518     }\r
5519     break;\r
5520 \r
5521   case WM_MOVING:\r
5522     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5523       return OnMoving( &sd, hwnd, wParam, lParam );\r
5524 \r
5525   case WM_EXITSIZEMOVE:\r
5526     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5527     if (hwnd == hwndMain) {\r
5528       RECT client;\r
5529       doingSizing = FALSE;\r
5530       InvalidateRect(hwnd, &boardRect, FALSE);\r
5531       GetClientRect(hwnd, &client);\r
5532       ResizeBoard(client.right, client.bottom, lastSizing);\r
5533       lastSizing = 0;\r
5534       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5535     }\r
5536     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5537     break;\r
5538 \r
5539   case WM_DESTROY: /* message: window being destroyed */\r
5540     PostQuitMessage(0);\r
5541     break;\r
5542 \r
5543   case WM_CLOSE:\r
5544     if (hwnd == hwndMain) {\r
5545       ExitEvent(0);\r
5546     }\r
5547     break;\r
5548 \r
5549   default:      /* Passes it on if unprocessed */\r
5550     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5551   }\r
5552   return 0;\r
5553 }\r
5554 \r
5555 /*---------------------------------------------------------------------------*\\r
5556  *\r
5557  * Misc utility routines\r
5558  *\r
5559 \*---------------------------------------------------------------------------*/\r
5560 \r
5561 /*\r
5562  * Decent random number generator, at least not as bad as Windows\r
5563  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5564  */\r
5565 unsigned int randstate;\r
5566 \r
5567 int\r
5568 myrandom(void)\r
5569 {\r
5570   randstate = randstate * 1664525 + 1013904223;\r
5571   return (int) randstate & 0x7fffffff;\r
5572 }\r
5573 \r
5574 void\r
5575 mysrandom(unsigned int seed)\r
5576 {\r
5577   randstate = seed;\r
5578 }\r
5579 \r
5580 \r
5581 /* \r
5582  * returns TRUE if user selects a different color, FALSE otherwise \r
5583  */\r
5584 \r
5585 BOOL\r
5586 ChangeColor(HWND hwnd, COLORREF *which)\r
5587 {\r
5588   static BOOL firstTime = TRUE;\r
5589   static DWORD customColors[16];\r
5590   CHOOSECOLOR cc;\r
5591   COLORREF newcolor;\r
5592   int i;\r
5593   ColorClass ccl;\r
5594 \r
5595   if (firstTime) {\r
5596     /* Make initial colors in use available as custom colors */\r
5597     /* Should we put the compiled-in defaults here instead? */\r
5598     i = 0;\r
5599     customColors[i++] = lightSquareColor & 0xffffff;\r
5600     customColors[i++] = darkSquareColor & 0xffffff;\r
5601     customColors[i++] = whitePieceColor & 0xffffff;\r
5602     customColors[i++] = blackPieceColor & 0xffffff;\r
5603     customColors[i++] = highlightSquareColor & 0xffffff;\r
5604     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5605 \r
5606     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5607       customColors[i++] = textAttribs[ccl].color;\r
5608     }\r
5609     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5610     firstTime = FALSE;\r
5611   }\r
5612 \r
5613   cc.lStructSize = sizeof(cc);\r
5614   cc.hwndOwner = hwnd;\r
5615   cc.hInstance = NULL;\r
5616   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5617   cc.lpCustColors = (LPDWORD) customColors;\r
5618   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5619 \r
5620   if (!ChooseColor(&cc)) return FALSE;\r
5621 \r
5622   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5623   if (newcolor == *which) return FALSE;\r
5624   *which = newcolor;\r
5625   return TRUE;\r
5626 \r
5627   /*\r
5628   InitDrawingColors();\r
5629   InvalidateRect(hwnd, &boardRect, FALSE);\r
5630   */\r
5631 }\r
5632 \r
5633 BOOLEAN\r
5634 MyLoadSound(MySound *ms)\r
5635 {\r
5636   BOOL ok = FALSE;\r
5637   struct stat st;\r
5638   FILE *f;\r
5639 \r
5640   if (ms->data && ms->flag) free(ms->data);\r
5641   ms->data = NULL;\r
5642 \r
5643   switch (ms->name[0]) {\r
5644   case NULLCHAR:\r
5645     /* Silence */\r
5646     ok = TRUE;\r
5647     break;\r
5648   case '$':\r
5649     /* System sound from Control Panel.  Don't preload here. */\r
5650     ok = TRUE;\r
5651     break;\r
5652   case '!':\r
5653     if (ms->name[1] == NULLCHAR) {\r
5654       /* "!" alone = silence */\r
5655       ok = TRUE;\r
5656     } else {\r
5657       /* Builtin wave resource.  Error if not found. */\r
5658       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5659       if (h == NULL) break;\r
5660       ms->data = (void *)LoadResource(hInst, h);\r
5661       ms->flag = 0; // not maloced, so cannot be freed!\r
5662       if (h == NULL) break;\r
5663       ok = TRUE;\r
5664     }\r
5665     break;\r
5666   default:\r
5667     /* .wav file.  Error if not found. */\r
5668     f = fopen(ms->name, "rb");\r
5669     if (f == NULL) break;\r
5670     if (fstat(fileno(f), &st) < 0) break;\r
5671     ms->data = malloc(st.st_size);\r
5672     ms->flag = 1;\r
5673     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5674     fclose(f);\r
5675     ok = TRUE;\r
5676     break;\r
5677   }\r
5678   if (!ok) {\r
5679     char buf[MSG_SIZ];\r
5680       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5681     DisplayError(buf, GetLastError());\r
5682   }\r
5683   return ok;\r
5684 }\r
5685 \r
5686 BOOLEAN\r
5687 MyPlaySound(MySound *ms)\r
5688 {\r
5689   BOOLEAN ok = FALSE;\r
5690 \r
5691   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5692   switch (ms->name[0]) {\r
5693   case NULLCHAR:\r
5694         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5695     /* Silence */\r
5696     ok = TRUE;\r
5697     break;\r
5698   case '$':\r
5699     /* System sound from Control Panel (deprecated feature).\r
5700        "$" alone or an unset sound name gets default beep (still in use). */\r
5701     if (ms->name[1]) {\r
5702       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5703     }\r
5704     if (!ok) ok = MessageBeep(MB_OK);\r
5705     break; \r
5706   case '!':\r
5707     /* Builtin wave resource, or "!" alone for silence */\r
5708     if (ms->name[1]) {\r
5709       if (ms->data == NULL) return FALSE;\r
5710       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5711     } else {\r
5712       ok = TRUE;\r
5713     }\r
5714     break;\r
5715   default:\r
5716     /* .wav file.  Error if not found. */\r
5717     if (ms->data == NULL) return FALSE;\r
5718     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5719     break;\r
5720   }\r
5721   /* Don't print an error: this can happen innocently if the sound driver\r
5722      is busy; for instance, if another instance of WinBoard is playing\r
5723      a sound at about the same time. */\r
5724   return ok;\r
5725 }\r
5726 \r
5727 \r
5728 LRESULT CALLBACK\r
5729 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5730 {\r
5731   BOOL ok;\r
5732   OPENFILENAME *ofn;\r
5733   static UINT *number; /* gross that this is static */\r
5734 \r
5735   switch (message) {\r
5736   case WM_INITDIALOG: /* message: initialize dialog box */\r
5737     /* Center the dialog over the application window */\r
5738     ofn = (OPENFILENAME *) lParam;\r
5739     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5740       number = (UINT *) ofn->lCustData;\r
5741       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5742     } else {\r
5743       number = NULL;\r
5744     }\r
5745     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5746     Translate(hDlg, 1536);\r
5747     return FALSE;  /* Allow for further processing */\r
5748 \r
5749   case WM_COMMAND:\r
5750     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5751       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5752     }\r
5753     return FALSE;  /* Allow for further processing */\r
5754   }\r
5755   return FALSE;\r
5756 }\r
5757 \r
5758 UINT APIENTRY\r
5759 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5760 {\r
5761   static UINT *number;\r
5762   OPENFILENAME *ofname;\r
5763   OFNOTIFY *ofnot;\r
5764   switch (uiMsg) {\r
5765   case WM_INITDIALOG:\r
5766     Translate(hdlg, DLG_IndexNumber);\r
5767     ofname = (OPENFILENAME *)lParam;\r
5768     number = (UINT *)(ofname->lCustData);\r
5769     break;\r
5770   case WM_NOTIFY:\r
5771     ofnot = (OFNOTIFY *)lParam;\r
5772     if (ofnot->hdr.code == CDN_FILEOK) {\r
5773       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5774     }\r
5775     break;\r
5776   }\r
5777   return 0;\r
5778 }\r
5779 \r
5780 \r
5781 FILE *\r
5782 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5783                char *nameFilt, char *dlgTitle, UINT *number,\r
5784                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5785 {\r
5786   OPENFILENAME openFileName;\r
5787   char buf1[MSG_SIZ];\r
5788   FILE *f;\r
5789 \r
5790   if (fileName == NULL) fileName = buf1;\r
5791   if (defName == NULL) {\r
5792     safeStrCpy(fileName, "*.", 3 );\r
5793     strcat(fileName, defExt);\r
5794   } else {\r
5795     safeStrCpy(fileName, defName, MSG_SIZ );\r
5796   }\r
5797     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5798   if (number) *number = 0;\r
5799 \r
5800   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5801   openFileName.hwndOwner         = hwnd;\r
5802   openFileName.hInstance         = (HANDLE) hInst;\r
5803   openFileName.lpstrFilter       = nameFilt;\r
5804   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5805   openFileName.nMaxCustFilter    = 0L;\r
5806   openFileName.nFilterIndex      = 1L;\r
5807   openFileName.lpstrFile         = fileName;\r
5808   openFileName.nMaxFile          = MSG_SIZ;\r
5809   openFileName.lpstrFileTitle    = fileTitle;\r
5810   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5811   openFileName.lpstrInitialDir   = NULL;\r
5812   openFileName.lpstrTitle        = dlgTitle;\r
5813   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5814     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5815     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5816     | (oldDialog ? 0 : OFN_EXPLORER);\r
5817   openFileName.nFileOffset       = 0;\r
5818   openFileName.nFileExtension    = 0;\r
5819   openFileName.lpstrDefExt       = defExt;\r
5820   openFileName.lCustData         = (LONG) number;\r
5821   openFileName.lpfnHook          = oldDialog ?\r
5822     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5823   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5824 \r
5825   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5826                         GetOpenFileName(&openFileName)) {\r
5827     /* open the file */\r
5828     f = fopen(openFileName.lpstrFile, write);\r
5829     if (f == NULL) {\r
5830       MessageBox(hwnd, _("File open failed"), NULL,\r
5831                  MB_OK|MB_ICONEXCLAMATION);\r
5832       return NULL;\r
5833     }\r
5834   } else {\r
5835     int err = CommDlgExtendedError();\r
5836     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5837     return FALSE;\r
5838   }\r
5839   return f;\r
5840 }\r
5841 \r
5842 \r
5843 \r
5844 VOID APIENTRY\r
5845 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5846 {\r
5847   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5848 \r
5849   /*\r
5850    * Get the first pop-up menu in the menu template. This is the\r
5851    * menu that TrackPopupMenu displays.\r
5852    */\r
5853   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5854   TranslateOneMenu(10, hmenuTrackPopup);\r
5855 \r
5856   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5857 \r
5858   /*\r
5859    * TrackPopup uses screen coordinates, so convert the\r
5860    * coordinates of the mouse click to screen coordinates.\r
5861    */\r
5862   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5863 \r
5864   /* Draw and track the floating pop-up menu. */\r
5865   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5866                  pt.x, pt.y, 0, hwnd, NULL);\r
5867 \r
5868   /* Destroy the menu.*/\r
5869   DestroyMenu(hmenu);\r
5870 }\r
5871    \r
5872 typedef struct {\r
5873   HWND hDlg, hText;\r
5874   int sizeX, sizeY, newSizeX, newSizeY;\r
5875   HDWP hdwp;\r
5876 } ResizeEditPlusButtonsClosure;\r
5877 \r
5878 BOOL CALLBACK\r
5879 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5880 {\r
5881   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5882   RECT rect;\r
5883   POINT pt;\r
5884 \r
5885   if (hChild == cl->hText) return TRUE;\r
5886   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5887   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5888   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5889   ScreenToClient(cl->hDlg, &pt);\r
5890   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5891     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5892   return TRUE;\r
5893 }\r
5894 \r
5895 /* Resize a dialog that has a (rich) edit field filling most of\r
5896    the top, with a row of buttons below */\r
5897 VOID\r
5898 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5899 {\r
5900   RECT rectText;\r
5901   int newTextHeight, newTextWidth;\r
5902   ResizeEditPlusButtonsClosure cl;\r
5903   \r
5904   /*if (IsIconic(hDlg)) return;*/\r
5905   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5906   \r
5907   cl.hdwp = BeginDeferWindowPos(8);\r
5908 \r
5909   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5910   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5911   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5912   if (newTextHeight < 0) {\r
5913     newSizeY += -newTextHeight;\r
5914     newTextHeight = 0;\r
5915   }\r
5916   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5917     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5918 \r
5919   cl.hDlg = hDlg;\r
5920   cl.hText = hText;\r
5921   cl.sizeX = sizeX;\r
5922   cl.sizeY = sizeY;\r
5923   cl.newSizeX = newSizeX;\r
5924   cl.newSizeY = newSizeY;\r
5925   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5926 \r
5927   EndDeferWindowPos(cl.hdwp);\r
5928 }\r
5929 \r
5930 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5931 {\r
5932     RECT    rChild, rParent;\r
5933     int     wChild, hChild, wParent, hParent;\r
5934     int     wScreen, hScreen, xNew, yNew;\r
5935     HDC     hdc;\r
5936 \r
5937     /* Get the Height and Width of the child window */\r
5938     GetWindowRect (hwndChild, &rChild);\r
5939     wChild = rChild.right - rChild.left;\r
5940     hChild = rChild.bottom - rChild.top;\r
5941 \r
5942     /* Get the Height and Width of the parent window */\r
5943     GetWindowRect (hwndParent, &rParent);\r
5944     wParent = rParent.right - rParent.left;\r
5945     hParent = rParent.bottom - rParent.top;\r
5946 \r
5947     /* Get the display limits */\r
5948     hdc = GetDC (hwndChild);\r
5949     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5950     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5951     ReleaseDC(hwndChild, hdc);\r
5952 \r
5953     /* Calculate new X position, then adjust for screen */\r
5954     xNew = rParent.left + ((wParent - wChild) /2);\r
5955     if (xNew < 0) {\r
5956         xNew = 0;\r
5957     } else if ((xNew+wChild) > wScreen) {\r
5958         xNew = wScreen - wChild;\r
5959     }\r
5960 \r
5961     /* Calculate new Y position, then adjust for screen */\r
5962     if( mode == 0 ) {\r
5963         yNew = rParent.top  + ((hParent - hChild) /2);\r
5964     }\r
5965     else {\r
5966         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5967     }\r
5968 \r
5969     if (yNew < 0) {\r
5970         yNew = 0;\r
5971     } else if ((yNew+hChild) > hScreen) {\r
5972         yNew = hScreen - hChild;\r
5973     }\r
5974 \r
5975     /* Set it, and return */\r
5976     return SetWindowPos (hwndChild, NULL,\r
5977                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5978 }\r
5979 \r
5980 /* Center one window over another */\r
5981 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5982 {\r
5983     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5984 }\r
5985 \r
5986 /*---------------------------------------------------------------------------*\\r
5987  *\r
5988  * Startup Dialog functions\r
5989  *\r
5990 \*---------------------------------------------------------------------------*/\r
5991 void\r
5992 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5993 {\r
5994   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5995 \r
5996   while (*cd != NULL) {\r
5997     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5998     cd++;\r
5999   }\r
6000 }\r
6001 \r
6002 void\r
6003 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6004 {\r
6005   char buf1[MAX_ARG_LEN];\r
6006   int len;\r
6007 \r
6008   if (str[0] == '@') {\r
6009     FILE* f = fopen(str + 1, "r");\r
6010     if (f == NULL) {\r
6011       DisplayFatalError(str + 1, errno, 2);\r
6012       return;\r
6013     }\r
6014     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6015     fclose(f);\r
6016     buf1[len] = NULLCHAR;\r
6017     str = buf1;\r
6018   }\r
6019 \r
6020   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6021 \r
6022   for (;;) {\r
6023     char buf[MSG_SIZ];\r
6024     char *end = strchr(str, '\n');\r
6025     if (end == NULL) return;\r
6026     memcpy(buf, str, end - str);\r
6027     buf[end - str] = NULLCHAR;\r
6028     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6029     str = end + 1;\r
6030   }\r
6031 }\r
6032 \r
6033 void\r
6034 SetStartupDialogEnables(HWND hDlg)\r
6035 {\r
6036   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6037     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6038     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6039   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6040     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6041   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6042     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6043   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6044     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6045   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6046     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6047     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6048     IsDlgButtonChecked(hDlg, OPT_View));\r
6049 }\r
6050 \r
6051 char *\r
6052 QuoteForFilename(char *filename)\r
6053 {\r
6054   int dquote, space;\r
6055   dquote = strchr(filename, '"') != NULL;\r
6056   space = strchr(filename, ' ') != NULL;\r
6057   if (dquote || space) {\r
6058     if (dquote) {\r
6059       return "'";\r
6060     } else {\r
6061       return "\"";\r
6062     }\r
6063   } else {\r
6064     return "";\r
6065   }\r
6066 }\r
6067 \r
6068 VOID\r
6069 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6070 {\r
6071   char buf[MSG_SIZ];\r
6072   char *q;\r
6073 \r
6074   InitComboStringsFromOption(hwndCombo, nthnames);\r
6075   q = QuoteForFilename(nthcp);\r
6076     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6077   if (*nthdir != NULLCHAR) {\r
6078     q = QuoteForFilename(nthdir);\r
6079       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6080   }\r
6081   if (*nthcp == NULLCHAR) {\r
6082     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6083   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6084     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6085     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6086   }\r
6087 }\r
6088 \r
6089 LRESULT CALLBACK\r
6090 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6091 {\r
6092   char buf[MSG_SIZ];\r
6093   HANDLE hwndCombo;\r
6094   char *p;\r
6095 \r
6096   switch (message) {\r
6097   case WM_INITDIALOG:\r
6098     /* Center the dialog */\r
6099     CenterWindow (hDlg, GetDesktopWindow());\r
6100     Translate(hDlg, DLG_Startup);\r
6101     /* Initialize the dialog items */\r
6102     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6103                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6104                   firstChessProgramNames);\r
6105     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6106                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6107                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6108     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6109     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6110       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6111     if (*appData.icsHelper != NULLCHAR) {\r
6112       char *q = QuoteForFilename(appData.icsHelper);\r
6113       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6114     }\r
6115     if (*appData.icsHost == NULLCHAR) {\r
6116       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6117       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6118     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6119       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6120       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6121     }\r
6122 \r
6123     if (appData.icsActive) {\r
6124       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6125     }\r
6126     else if (appData.noChessProgram) {\r
6127       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6128     }\r
6129     else {\r
6130       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6131     }\r
6132 \r
6133     SetStartupDialogEnables(hDlg);\r
6134     return TRUE;\r
6135 \r
6136   case WM_COMMAND:\r
6137     switch (LOWORD(wParam)) {\r
6138     case IDOK:\r
6139       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6140         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6141         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6142         p = buf;\r
6143         ParseArgs(StringGet, &p);\r
6144         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6145         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6146         p = buf;
6147         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6148         ParseArgs(StringGet, &p);\r
6149         SwapEngines(singleList); // ... and then make it 'second'\r
6150         appData.noChessProgram = FALSE;\r
6151         appData.icsActive = FALSE;\r
6152       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6153         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6154         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6155         p = buf;\r
6156         ParseArgs(StringGet, &p);\r
6157         if (appData.zippyPlay) {\r
6158           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6159           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6160           p = buf;\r
6161           ParseArgs(StringGet, &p);\r
6162         }\r
6163       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6164         appData.noChessProgram = TRUE;\r
6165         appData.icsActive = FALSE;\r
6166       } else {\r
6167         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6168                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6169         return TRUE;\r
6170       }\r
6171       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6172         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6173         p = buf;\r
6174         ParseArgs(StringGet, &p);\r
6175       }\r
6176       EndDialog(hDlg, TRUE);\r
6177       return TRUE;\r
6178 \r
6179     case IDCANCEL:\r
6180       ExitEvent(0);\r
6181       return TRUE;\r
6182 \r
6183     case IDM_HELPCONTENTS:\r
6184       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6185         MessageBox (GetFocus(),\r
6186                     _("Unable to activate help"),\r
6187                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6188       }\r
6189       break;\r
6190 \r
6191     default:\r
6192       SetStartupDialogEnables(hDlg);\r
6193       break;\r
6194     }\r
6195     break;\r
6196   }\r
6197   return FALSE;\r
6198 }\r
6199 \r
6200 /*---------------------------------------------------------------------------*\\r
6201  *\r
6202  * About box dialog functions\r
6203  *\r
6204 \*---------------------------------------------------------------------------*/\r
6205 \r
6206 /* Process messages for "About" dialog box */\r
6207 LRESULT CALLBACK\r
6208 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6209 {\r
6210   switch (message) {\r
6211   case WM_INITDIALOG: /* message: initialize dialog box */\r
6212     /* Center the dialog over the application window */\r
6213     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6214     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6215     Translate(hDlg, ABOUTBOX);\r
6216     JAWS_COPYRIGHT\r
6217     return (TRUE);\r
6218 \r
6219   case WM_COMMAND: /* message: received a command */\r
6220     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6221         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6222       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6223       return (TRUE);\r
6224     }\r
6225     break;\r
6226   }\r
6227   return (FALSE);\r
6228 }\r
6229 \r
6230 /*---------------------------------------------------------------------------*\\r
6231  *\r
6232  * Comment Dialog functions\r
6233  *\r
6234 \*---------------------------------------------------------------------------*/\r
6235 \r
6236 LRESULT CALLBACK\r
6237 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6238 {\r
6239   static HANDLE hwndText = NULL;\r
6240   int len, newSizeX, newSizeY, flags;\r
6241   static int sizeX, sizeY;\r
6242   char *str;\r
6243   RECT rect;\r
6244   MINMAXINFO *mmi;\r
6245 \r
6246   switch (message) {\r
6247   case WM_INITDIALOG: /* message: initialize dialog box */\r
6248     /* Initialize the dialog items */\r
6249     Translate(hDlg, DLG_EditComment);\r
6250     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6251     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6252     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6253     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6254     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6255     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6256     SetWindowText(hDlg, commentTitle);\r
6257     if (editComment) {\r
6258       SetFocus(hwndText);\r
6259     } else {\r
6260       SetFocus(GetDlgItem(hDlg, IDOK));\r
6261     }\r
6262     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6263                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6264                 MAKELPARAM(FALSE, 0));\r
6265     /* Size and position the dialog */\r
6266     if (!commentDialog) {\r
6267       commentDialog = hDlg;\r
6268       flags = SWP_NOZORDER;\r
6269       GetClientRect(hDlg, &rect);\r
6270       sizeX = rect.right;\r
6271       sizeY = rect.bottom;\r
6272       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6273           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6274         WINDOWPLACEMENT wp;\r
6275         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6276         wp.length = sizeof(WINDOWPLACEMENT);\r
6277         wp.flags = 0;\r
6278         wp.showCmd = SW_SHOW;\r
6279         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6280         wp.rcNormalPosition.left = wpComment.x;\r
6281         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6282         wp.rcNormalPosition.top = wpComment.y;\r
6283         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6284         SetWindowPlacement(hDlg, &wp);\r
6285 \r
6286         GetClientRect(hDlg, &rect);\r
6287         newSizeX = rect.right;\r
6288         newSizeY = rect.bottom;\r
6289         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6290                               newSizeX, newSizeY);\r
6291         sizeX = newSizeX;\r
6292         sizeY = newSizeY;\r
6293       }\r
6294     }\r
6295     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6296     return FALSE;\r
6297 \r
6298   case WM_COMMAND: /* message: received a command */\r
6299     switch (LOWORD(wParam)) {\r
6300     case IDOK:\r
6301       if (editComment) {\r
6302         char *p, *q;\r
6303         /* Read changed options from the dialog box */\r
6304         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6305         len = GetWindowTextLength(hwndText);\r
6306         str = (char *) malloc(len + 1);\r
6307         GetWindowText(hwndText, str, len + 1);\r
6308         p = q = str;\r
6309         while (*q) {\r
6310           if (*q == '\r')\r
6311             q++;\r
6312           else\r
6313             *p++ = *q++;\r
6314         }\r
6315         *p = NULLCHAR;\r
6316         ReplaceComment(commentIndex, str);\r
6317         free(str);\r
6318       }\r
6319       CommentPopDown();\r
6320       return TRUE;\r
6321 \r
6322     case IDCANCEL:\r
6323     case OPT_CancelComment:\r
6324       CommentPopDown();\r
6325       return TRUE;\r
6326 \r
6327     case OPT_ClearComment:\r
6328       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6329       break;\r
6330 \r
6331     case OPT_EditComment:\r
6332       EditCommentEvent();\r
6333       return TRUE;\r
6334 \r
6335     default:\r
6336       break;\r
6337     }\r
6338     break;\r
6339 \r
6340   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6341         if( wParam == OPT_CommentText ) {\r
6342             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6343 \r
6344             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6345                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6346                 POINTL pt;\r
6347                 LRESULT index;\r
6348 \r
6349                 pt.x = LOWORD( lpMF->lParam );\r
6350                 pt.y = HIWORD( lpMF->lParam );\r
6351 \r
6352                 if(lpMF->msg == WM_CHAR) {\r
6353                         CHARRANGE sel;\r
6354                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6355                         index = sel.cpMin;\r
6356                 } else\r
6357                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6358 \r
6359                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6360                 len = GetWindowTextLength(hwndText);\r
6361                 str = (char *) malloc(len + 1);\r
6362                 GetWindowText(hwndText, str, len + 1);\r
6363                 ReplaceComment(commentIndex, str);\r
6364                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6365                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6366                 free(str);\r
6367 \r
6368                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6369                 lpMF->msg = WM_USER;\r
6370 \r
6371                 return TRUE;\r
6372             }\r
6373         }\r
6374         break;\r
6375 \r
6376   case WM_SIZE:\r
6377     newSizeX = LOWORD(lParam);\r
6378     newSizeY = HIWORD(lParam);\r
6379     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6380     sizeX = newSizeX;\r
6381     sizeY = newSizeY;\r
6382     break;\r
6383 \r
6384   case WM_GETMINMAXINFO:\r
6385     /* Prevent resizing window too small */\r
6386     mmi = (MINMAXINFO *) lParam;\r
6387     mmi->ptMinTrackSize.x = 100;\r
6388     mmi->ptMinTrackSize.y = 100;\r
6389     break;\r
6390   }\r
6391   return FALSE;\r
6392 }\r
6393 \r
6394 VOID\r
6395 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6396 {\r
6397   FARPROC lpProc;\r
6398   char *p, *q;\r
6399 \r
6400   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6401 \r
6402   if (str == NULL) str = "";\r
6403   p = (char *) malloc(2 * strlen(str) + 2);\r
6404   q = p;\r
6405   while (*str) {\r
6406     if (*str == '\n') *q++ = '\r';\r
6407     *q++ = *str++;\r
6408   }\r
6409   *q = NULLCHAR;\r
6410   if (commentText != NULL) free(commentText);\r
6411 \r
6412   commentIndex = index;\r
6413   commentTitle = title;\r
6414   commentText = p;\r
6415   editComment = edit;\r
6416 \r
6417   if (commentDialog) {\r
6418     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6419     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6420   } else {\r
6421     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6422     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6423                  hwndMain, (DLGPROC)lpProc);\r
6424     FreeProcInstance(lpProc);\r
6425   }\r
6426   commentUp = TRUE;\r
6427 }\r
6428 \r
6429 \r
6430 /*---------------------------------------------------------------------------*\\r
6431  *\r
6432  * Type-in move dialog functions\r
6433  * \r
6434 \*---------------------------------------------------------------------------*/\r
6435 \r
6436 LRESULT CALLBACK\r
6437 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6438 {\r
6439   char move[MSG_SIZ];\r
6440   HWND hInput;\r
6441 \r
6442   switch (message) {\r
6443   case WM_INITDIALOG:\r
6444     move[0] = (char) lParam;\r
6445     move[1] = NULLCHAR;\r
6446     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6447     Translate(hDlg, DLG_TypeInMove);\r
6448     hInput = GetDlgItem(hDlg, OPT_Move);\r
6449     SetWindowText(hInput, move);\r
6450     SetFocus(hInput);\r
6451     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6452     return FALSE;\r
6453 \r
6454   case WM_COMMAND:\r
6455     switch (LOWORD(wParam)) {\r
6456     case IDOK:
6457 \r
6458       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6459       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6460       TypeInDoneEvent(move);\r
6461       EndDialog(hDlg, TRUE);\r
6462       return TRUE;\r
6463     case IDCANCEL:\r
6464       EndDialog(hDlg, FALSE);\r
6465       return TRUE;\r
6466     default:\r
6467       break;\r
6468     }\r
6469     break;\r
6470   }\r
6471   return FALSE;\r
6472 }\r
6473 \r
6474 VOID\r
6475 PopUpMoveDialog(char firstchar)\r
6476 {\r
6477     FARPROC lpProc;\r
6478 \r
6479       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6480       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6481         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6482       FreeProcInstance(lpProc);\r
6483 }\r
6484 \r
6485 /*---------------------------------------------------------------------------*\\r
6486  *\r
6487  * Type-in name dialog functions\r
6488  * \r
6489 \*---------------------------------------------------------------------------*/\r
6490 \r
6491 LRESULT CALLBACK\r
6492 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6493 {\r
6494   char move[MSG_SIZ];\r
6495   HWND hInput;\r
6496 \r
6497   switch (message) {\r
6498   case WM_INITDIALOG:\r
6499     move[0] = (char) lParam;\r
6500     move[1] = NULLCHAR;\r
6501     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6502     Translate(hDlg, DLG_TypeInName);\r
6503     hInput = GetDlgItem(hDlg, OPT_Name);\r
6504     SetWindowText(hInput, move);\r
6505     SetFocus(hInput);\r
6506     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6507     return FALSE;\r
6508 \r
6509   case WM_COMMAND:\r
6510     switch (LOWORD(wParam)) {\r
6511     case IDOK:\r
6512       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6513       appData.userName = strdup(move);\r
6514       SetUserLogo();\r
6515       SetGameInfo();\r
6516       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6517         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6518         DisplayTitle(move);\r
6519       }\r
6520 \r
6521 \r
6522       EndDialog(hDlg, TRUE);\r
6523       return TRUE;\r
6524     case IDCANCEL:\r
6525       EndDialog(hDlg, FALSE);\r
6526       return TRUE;\r
6527     default:\r
6528       break;\r
6529     }\r
6530     break;\r
6531   }\r
6532   return FALSE;\r
6533 }\r
6534 \r
6535 VOID\r
6536 PopUpNameDialog(char firstchar)\r
6537 {\r
6538     FARPROC lpProc;\r
6539     \r
6540       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6541       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6542         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6543       FreeProcInstance(lpProc);\r
6544 }\r
6545 \r
6546 /*---------------------------------------------------------------------------*\\r
6547  *\r
6548  *  Error dialogs\r
6549  * \r
6550 \*---------------------------------------------------------------------------*/\r
6551 \r
6552 /* Nonmodal error box */\r
6553 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6554                              WPARAM wParam, LPARAM lParam);\r
6555 \r
6556 VOID\r
6557 ErrorPopUp(char *title, char *content)\r
6558 {\r
6559   FARPROC lpProc;\r
6560   char *p, *q;\r
6561   BOOLEAN modal = hwndMain == NULL;\r
6562 \r
6563   p = content;\r
6564   q = errorMessage;\r
6565   while (*p) {\r
6566     if (*p == '\n') {\r
6567       if (modal) {\r
6568         *q++ = ' ';\r
6569         p++;\r
6570       } else {\r
6571         *q++ = '\r';\r
6572         *q++ = *p++;\r
6573       }\r
6574     } else {\r
6575       *q++ = *p++;\r
6576     }\r
6577   }\r
6578   *q = NULLCHAR;\r
6579   strncpy(errorTitle, title, sizeof(errorTitle));\r
6580   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6581   \r
6582   if (modal) {\r
6583     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6584   } else {\r
6585     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6586     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6587                  hwndMain, (DLGPROC)lpProc);\r
6588     FreeProcInstance(lpProc);\r
6589   }\r
6590 }\r
6591 \r
6592 VOID\r
6593 ErrorPopDown()\r
6594 {\r
6595   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6596   if (errorDialog == NULL) return;\r
6597   DestroyWindow(errorDialog);\r
6598   errorDialog = NULL;\r
6599   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6600 }\r
6601 \r
6602 LRESULT CALLBACK\r
6603 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6604 {\r
6605   HANDLE hwndText;\r
6606   RECT rChild;\r
6607 \r
6608   switch (message) {\r
6609   case WM_INITDIALOG:\r
6610     GetWindowRect(hDlg, &rChild);\r
6611 \r
6612     /*\r
6613     SetWindowPos(hDlg, NULL, rChild.left,\r
6614       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6615       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6616     */\r
6617 \r
6618     /* \r
6619         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6620         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6621         and it doesn't work when you resize the dialog.\r
6622         For now, just give it a default position.\r
6623     */\r
6624     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6625     Translate(hDlg, DLG_Error);\r
6626 \r
6627     errorDialog = hDlg;\r
6628     SetWindowText(hDlg, errorTitle);\r
6629     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6630     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6631     return FALSE;\r
6632 \r
6633   case WM_COMMAND:\r
6634     switch (LOWORD(wParam)) {\r
6635     case IDOK:\r
6636     case IDCANCEL:\r
6637       if (errorDialog == hDlg) errorDialog = NULL;\r
6638       DestroyWindow(hDlg);\r
6639       return TRUE;\r
6640 \r
6641     default:\r
6642       break;\r
6643     }\r
6644     break;\r
6645   }\r
6646   return FALSE;\r
6647 }\r
6648 \r
6649 #ifdef GOTHIC\r
6650 HWND gothicDialog = NULL;\r
6651 \r
6652 LRESULT CALLBACK\r
6653 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6654 {\r
6655   HANDLE hwndText;\r
6656   RECT rChild;\r
6657   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6658 \r
6659   switch (message) {\r
6660   case WM_INITDIALOG:\r
6661     GetWindowRect(hDlg, &rChild);\r
6662 \r
6663     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6664                                                              SWP_NOZORDER);\r
6665 \r
6666     /* \r
6667         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6668         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6669         and it doesn't work when you resize the dialog.\r
6670         For now, just give it a default position.\r
6671     */\r
6672     gothicDialog = hDlg;\r
6673     SetWindowText(hDlg, errorTitle);\r
6674     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6675     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6676     return FALSE;\r
6677 \r
6678   case WM_COMMAND:\r
6679     switch (LOWORD(wParam)) {\r
6680     case IDOK:\r
6681     case IDCANCEL:\r
6682       if (errorDialog == hDlg) errorDialog = NULL;\r
6683       DestroyWindow(hDlg);\r
6684       return TRUE;\r
6685 \r
6686     default:\r
6687       break;\r
6688     }\r
6689     break;\r
6690   }\r
6691   return FALSE;\r
6692 }\r
6693 \r
6694 VOID\r
6695 GothicPopUp(char *title, VariantClass variant)\r
6696 {\r
6697   FARPROC lpProc;\r
6698   static char *lastTitle;\r
6699 \r
6700   strncpy(errorTitle, title, sizeof(errorTitle));\r
6701   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6702 \r
6703   if(lastTitle != title && gothicDialog != NULL) {\r
6704     DestroyWindow(gothicDialog);\r
6705     gothicDialog = NULL;\r
6706   }\r
6707   if(variant != VariantNormal && gothicDialog == NULL) {\r
6708     title = lastTitle;\r
6709     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6710     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6711                  hwndMain, (DLGPROC)lpProc);\r
6712     FreeProcInstance(lpProc);\r
6713   }\r
6714 }\r
6715 #endif\r
6716 \r
6717 /*---------------------------------------------------------------------------*\\r
6718  *\r
6719  *  Ics Interaction console functions\r
6720  *\r
6721 \*---------------------------------------------------------------------------*/\r
6722 \r
6723 #define HISTORY_SIZE 64\r
6724 static char *history[HISTORY_SIZE];\r
6725 int histIn = 0, histP = 0;\r
6726 \r
6727 VOID\r
6728 SaveInHistory(char *cmd)\r
6729 {\r
6730   if (history[histIn] != NULL) {\r
6731     free(history[histIn]);\r
6732     history[histIn] = NULL;\r
6733   }\r
6734   if (*cmd == NULLCHAR) return;\r
6735   history[histIn] = StrSave(cmd);\r
6736   histIn = (histIn + 1) % HISTORY_SIZE;\r
6737   if (history[histIn] != NULL) {\r
6738     free(history[histIn]);\r
6739     history[histIn] = NULL;\r
6740   }\r
6741   histP = histIn;\r
6742 }\r
6743 \r
6744 char *\r
6745 PrevInHistory(char *cmd)\r
6746 {\r
6747   int newhp;\r
6748   if (histP == histIn) {\r
6749     if (history[histIn] != NULL) free(history[histIn]);\r
6750     history[histIn] = StrSave(cmd);\r
6751   }\r
6752   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6753   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6754   histP = newhp;\r
6755   return history[histP];\r
6756 }\r
6757 \r
6758 char *\r
6759 NextInHistory()\r
6760 {\r
6761   if (histP == histIn) return NULL;\r
6762   histP = (histP + 1) % HISTORY_SIZE;\r
6763   return history[histP];   \r
6764 }\r
6765 \r
6766 HMENU\r
6767 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6768 {\r
6769   HMENU hmenu, h;\r
6770   int i = 0;\r
6771   hmenu = LoadMenu(hInst, "TextMenu");\r
6772   h = GetSubMenu(hmenu, 0);\r
6773   while (e->item) {\r
6774     if (strcmp(e->item, "-") == 0) {\r
6775       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6776     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6777       int flags = MF_STRING, j = 0;\r
6778       if (e->item[0] == '|') {\r
6779         flags |= MF_MENUBARBREAK;\r
6780         j++;\r
6781       }\r
6782       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6783       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6784     }\r
6785     e++;\r
6786     i++;\r
6787   } \r
6788   return hmenu;\r
6789 }\r
6790 \r
6791 WNDPROC consoleTextWindowProc;\r
6792 \r
6793 void\r
6794 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6795 {\r
6796   char buf[MSG_SIZ], name[MSG_SIZ];\r
6797   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6798   CHARRANGE sel;\r
6799 \r
6800   if (!getname) {\r
6801     SetWindowText(hInput, command);\r
6802     if (immediate) {\r
6803       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6804     } else {\r
6805       sel.cpMin = 999999;\r
6806       sel.cpMax = 999999;\r
6807       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6808       SetFocus(hInput);\r
6809     }\r
6810     return;\r
6811   }    \r
6812   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6813   if (sel.cpMin == sel.cpMax) {\r
6814     /* Expand to surrounding word */\r
6815     TEXTRANGE tr;\r
6816     do {\r
6817       tr.chrg.cpMax = sel.cpMin;\r
6818       tr.chrg.cpMin = --sel.cpMin;\r
6819       if (sel.cpMin < 0) break;\r
6820       tr.lpstrText = name;\r
6821       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6822     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6823     sel.cpMin++;\r
6824 \r
6825     do {\r
6826       tr.chrg.cpMin = sel.cpMax;\r
6827       tr.chrg.cpMax = ++sel.cpMax;\r
6828       tr.lpstrText = name;\r
6829       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6830     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6831     sel.cpMax--;\r
6832 \r
6833     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6834       MessageBeep(MB_ICONEXCLAMATION);\r
6835       return;\r
6836     }\r
6837     tr.chrg = sel;\r
6838     tr.lpstrText = name;\r
6839     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6840   } else {\r
6841     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6842       MessageBeep(MB_ICONEXCLAMATION);\r
6843       return;\r
6844     }\r
6845     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6846   }\r
6847   if (immediate) {\r
6848     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6849     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6850     SetWindowText(hInput, buf);\r
6851     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6852   } else {\r
6853     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6854       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6855     SetWindowText(hInput, buf);\r
6856     sel.cpMin = 999999;\r
6857     sel.cpMax = 999999;\r
6858     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6859     SetFocus(hInput);\r
6860   }\r
6861 }\r
6862 \r
6863 LRESULT CALLBACK \r
6864 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6865 {\r
6866   HWND hInput;\r
6867   CHARRANGE sel;\r
6868 \r
6869   switch (message) {\r
6870   case WM_KEYDOWN:\r
6871     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6872     if(wParam=='R') return 0;\r
6873     switch (wParam) {\r
6874     case VK_PRIOR:\r
6875       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6876       return 0;\r
6877     case VK_NEXT:\r
6878       sel.cpMin = 999999;\r
6879       sel.cpMax = 999999;\r
6880       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6881       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6882       return 0;\r
6883     }\r
6884     break;\r
6885   case WM_CHAR:\r
6886    if(wParam != '\022') {\r
6887     if (wParam == '\t') {\r
6888       if (GetKeyState(VK_SHIFT) < 0) {\r
6889         /* shifted */\r
6890         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6891         if (buttonDesc[0].hwnd) {\r
6892           SetFocus(buttonDesc[0].hwnd);\r
6893         } else {\r
6894           SetFocus(hwndMain);\r
6895         }\r
6896       } else {\r
6897         /* unshifted */\r
6898         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6899       }\r
6900     } else {\r
6901       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6902       JAWS_DELETE( SetFocus(hInput); )\r
6903       SendMessage(hInput, message, wParam, lParam);\r
6904     }\r
6905     return 0;\r
6906    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6907    lParam = -1;\r
6908   case WM_RBUTTONDOWN:\r
6909     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6910       /* Move selection here if it was empty */\r
6911       POINT pt;\r
6912       pt.x = LOWORD(lParam);\r
6913       pt.y = HIWORD(lParam);\r
6914       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6915       if (sel.cpMin == sel.cpMax) {\r
6916         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6917         sel.cpMax = sel.cpMin;\r
6918         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6919       }\r
6920       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6921 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6922       POINT pt;\r
6923       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6924       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6925       if (sel.cpMin == sel.cpMax) {\r
6926         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6927         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6928       }\r
6929       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6930         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6931       }\r
6932       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6933       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6934       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6935       MenuPopup(hwnd, pt, hmenu, -1);\r
6936 }\r
6937     }\r
6938     return 0;\r
6939   case WM_RBUTTONUP:\r
6940     if (GetKeyState(VK_SHIFT) & ~1) {\r
6941       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6942         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6943     }\r
6944     return 0;\r
6945   case WM_PASTE:\r
6946     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6947     SetFocus(hInput);\r
6948     return SendMessage(hInput, message, wParam, lParam);\r
6949   case WM_MBUTTONDOWN:\r
6950     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6951   case WM_COMMAND:\r
6952     switch (LOWORD(wParam)) {\r
6953     case IDM_QuickPaste:\r
6954       {\r
6955         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6956         if (sel.cpMin == sel.cpMax) {\r
6957           MessageBeep(MB_ICONEXCLAMATION);\r
6958           return 0;\r
6959         }\r
6960         SendMessage(hwnd, WM_COPY, 0, 0);\r
6961         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6962         SendMessage(hInput, WM_PASTE, 0, 0);\r
6963         SetFocus(hInput);\r
6964         return 0;\r
6965       }\r
6966     case IDM_Cut:\r
6967       SendMessage(hwnd, WM_CUT, 0, 0);\r
6968       return 0;\r
6969     case IDM_Paste:\r
6970       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6971       return 0;\r
6972     case IDM_Copy:\r
6973       SendMessage(hwnd, WM_COPY, 0, 0);\r
6974       return 0;\r
6975     default:\r
6976       {\r
6977         int i = LOWORD(wParam) - IDM_CommandX;\r
6978         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6979             icsTextMenuEntry[i].command != NULL) {\r
6980           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6981                    icsTextMenuEntry[i].getname,\r
6982                    icsTextMenuEntry[i].immediate);\r
6983           return 0;\r
6984         }\r
6985       }\r
6986       break;\r
6987     }\r
6988     break;\r
6989   }\r
6990   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6991 }\r
6992 \r
6993 WNDPROC consoleInputWindowProc;\r
6994 \r
6995 LRESULT CALLBACK\r
6996 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6997 {\r
6998   char buf[MSG_SIZ];\r
6999   char *p;\r
7000   static BOOL sendNextChar = FALSE;\r
7001   static BOOL quoteNextChar = FALSE;\r
7002   InputSource *is = consoleInputSource;\r
7003   CHARFORMAT cf;\r
7004   CHARRANGE sel;\r
7005 \r
7006   switch (message) {\r
7007   case WM_CHAR:\r
7008     if (!appData.localLineEditing || sendNextChar) {\r
7009       is->buf[0] = (CHAR) wParam;\r
7010       is->count = 1;\r
7011       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7012       sendNextChar = FALSE;\r
7013       return 0;\r
7014     }\r
7015     if (quoteNextChar) {\r
7016       buf[0] = (char) wParam;\r
7017       buf[1] = NULLCHAR;\r
7018       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7019       quoteNextChar = FALSE;\r
7020       return 0;\r
7021     }\r
7022     switch (wParam) {\r
7023     case '\r':   /* Enter key */\r
7024       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7025       if (consoleEcho) SaveInHistory(is->buf);\r
7026       is->buf[is->count++] = '\n';\r
7027       is->buf[is->count] = NULLCHAR;\r
7028       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7029       if (consoleEcho) {\r
7030         ConsoleOutput(is->buf, is->count, TRUE);\r
7031       } else if (appData.localLineEditing) {\r
7032         ConsoleOutput("\n", 1, TRUE);\r
7033       }\r
7034       /* fall thru */\r
7035     case '\033': /* Escape key */\r
7036       SetWindowText(hwnd, "");\r
7037       cf.cbSize = sizeof(CHARFORMAT);\r
7038       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7039       if (consoleEcho) {\r
7040         cf.crTextColor = textAttribs[ColorNormal].color;\r
7041       } else {\r
7042         cf.crTextColor = COLOR_ECHOOFF;\r
7043       }\r
7044       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7045       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7046       return 0;\r
7047     case '\t':   /* Tab key */\r
7048       if (GetKeyState(VK_SHIFT) < 0) {\r
7049         /* shifted */\r
7050         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7051       } else {\r
7052         /* unshifted */\r
7053         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7054         if (buttonDesc[0].hwnd) {\r
7055           SetFocus(buttonDesc[0].hwnd);\r
7056         } else {\r
7057           SetFocus(hwndMain);\r
7058         }\r
7059       }\r
7060       return 0;\r
7061     case '\023': /* Ctrl+S */\r
7062       sendNextChar = TRUE;\r
7063       return 0;\r
7064     case '\021': /* Ctrl+Q */\r
7065       quoteNextChar = TRUE;\r
7066       return 0;\r
7067     JAWS_REPLAY\r
7068     default:\r
7069       break;\r
7070     }\r
7071     break;\r
7072   case WM_KEYDOWN:\r
7073     switch (wParam) {\r
7074     case VK_UP:\r
7075       GetWindowText(hwnd, buf, MSG_SIZ);\r
7076       p = PrevInHistory(buf);\r
7077       if (p != NULL) {\r
7078         SetWindowText(hwnd, p);\r
7079         sel.cpMin = 999999;\r
7080         sel.cpMax = 999999;\r
7081         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7082         return 0;\r
7083       }\r
7084       break;\r
7085     case VK_DOWN:\r
7086       p = NextInHistory();\r
7087       if (p != NULL) {\r
7088         SetWindowText(hwnd, p);\r
7089         sel.cpMin = 999999;\r
7090         sel.cpMax = 999999;\r
7091         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7092         return 0;\r
7093       }\r
7094       break;\r
7095     case VK_HOME:\r
7096     case VK_END:\r
7097       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7098       /* fall thru */\r
7099     case VK_PRIOR:\r
7100     case VK_NEXT:\r
7101       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7102       return 0;\r
7103     }\r
7104     break;\r
7105   case WM_MBUTTONDOWN:\r
7106     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7107       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7108     break;\r
7109   case WM_RBUTTONUP:\r
7110     if (GetKeyState(VK_SHIFT) & ~1) {\r
7111       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7112         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7113     } else {\r
7114       POINT pt;\r
7115       HMENU hmenu;\r
7116       hmenu = LoadMenu(hInst, "InputMenu");\r
7117       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7118       if (sel.cpMin == sel.cpMax) {\r
7119         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7120         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7121       }\r
7122       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7123         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7124       }\r
7125       pt.x = LOWORD(lParam);\r
7126       pt.y = HIWORD(lParam);\r
7127       MenuPopup(hwnd, pt, hmenu, -1);\r
7128     }\r
7129     return 0;\r
7130   case WM_COMMAND:\r
7131     switch (LOWORD(wParam)) { \r
7132     case IDM_Undo:\r
7133       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7134       return 0;\r
7135     case IDM_SelectAll:\r
7136       sel.cpMin = 0;\r
7137       sel.cpMax = -1; /*999999?*/\r
7138       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7139       return 0;\r
7140     case IDM_Cut:\r
7141       SendMessage(hwnd, WM_CUT, 0, 0);\r
7142       return 0;\r
7143     case IDM_Paste:\r
7144       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7145       return 0;\r
7146     case IDM_Copy:\r
7147       SendMessage(hwnd, WM_COPY, 0, 0);\r
7148       return 0;\r
7149     }\r
7150     break;\r
7151   }\r
7152   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7153 }\r
7154 \r
7155 #define CO_MAX  100000\r
7156 #define CO_TRIM   1000\r
7157 \r
7158 LRESULT CALLBACK\r
7159 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7160 {\r
7161   static SnapData sd;\r
7162   HWND hText, hInput;\r
7163   RECT rect;\r
7164   static int sizeX, sizeY;\r
7165   int newSizeX, newSizeY;\r
7166   MINMAXINFO *mmi;\r
7167   WORD wMask;\r
7168 \r
7169   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7170   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7171 \r
7172   switch (message) {\r
7173   case WM_NOTIFY:\r
7174     if (((NMHDR*)lParam)->code == EN_LINK)\r
7175     {\r
7176       ENLINK *pLink = (ENLINK*)lParam;\r
7177       if (pLink->msg == WM_LBUTTONUP)\r
7178       {\r
7179         TEXTRANGE tr;\r
7180 \r
7181         tr.chrg = pLink->chrg;\r
7182         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7183         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7184         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7185         free(tr.lpstrText);\r
7186       }\r
7187     }\r
7188     break;\r
7189   case WM_INITDIALOG: /* message: initialize dialog box */\r
7190     hwndConsole = hDlg;\r
7191     SetFocus(hInput);\r
7192     consoleTextWindowProc = (WNDPROC)\r
7193       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7194     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7195     consoleInputWindowProc = (WNDPROC)\r
7196       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7197     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7198     Colorize(ColorNormal, TRUE);\r
7199     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7200     ChangedConsoleFont();\r
7201     GetClientRect(hDlg, &rect);\r
7202     sizeX = rect.right;\r
7203     sizeY = rect.bottom;\r
7204     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7205         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7206       WINDOWPLACEMENT wp;\r
7207       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7208       wp.length = sizeof(WINDOWPLACEMENT);\r
7209       wp.flags = 0;\r
7210       wp.showCmd = SW_SHOW;\r
7211       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7212       wp.rcNormalPosition.left = wpConsole.x;\r
7213       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7214       wp.rcNormalPosition.top = wpConsole.y;\r
7215       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7216       SetWindowPlacement(hDlg, &wp);\r
7217     }\r
7218 \r
7219    // [HGM] Chessknight's change 2004-07-13\r
7220    else { /* Determine Defaults */\r
7221        WINDOWPLACEMENT wp;\r
7222        wpConsole.x = wpMain.width + 1;\r
7223        wpConsole.y = wpMain.y;\r
7224        wpConsole.width = screenWidth -  wpMain.width;\r
7225        wpConsole.height = wpMain.height;\r
7226        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7227        wp.length = sizeof(WINDOWPLACEMENT);\r
7228        wp.flags = 0;\r
7229        wp.showCmd = SW_SHOW;\r
7230        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7231        wp.rcNormalPosition.left = wpConsole.x;\r
7232        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7233        wp.rcNormalPosition.top = wpConsole.y;\r
7234        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7235        SetWindowPlacement(hDlg, &wp);\r
7236     }\r
7237 \r
7238    // Allow hText to highlight URLs and send notifications on them\r
7239    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7240    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7241    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7242    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7243 \r
7244     return FALSE;\r
7245 \r
7246   case WM_SETFOCUS:\r
7247     SetFocus(hInput);\r
7248     return 0;\r
7249 \r
7250   case WM_CLOSE:\r
7251     ExitEvent(0);\r
7252     /* not reached */\r
7253     break;\r
7254 \r
7255   case WM_SIZE:\r
7256     if (IsIconic(hDlg)) break;\r
7257     newSizeX = LOWORD(lParam);\r
7258     newSizeY = HIWORD(lParam);\r
7259     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7260       RECT rectText, rectInput;\r
7261       POINT pt;\r
7262       int newTextHeight, newTextWidth;\r
7263       GetWindowRect(hText, &rectText);\r
7264       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7265       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7266       if (newTextHeight < 0) {\r
7267         newSizeY += -newTextHeight;\r
7268         newTextHeight = 0;\r
7269       }\r
7270       SetWindowPos(hText, NULL, 0, 0,\r
7271         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7272       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7273       pt.x = rectInput.left;\r
7274       pt.y = rectInput.top + newSizeY - sizeY;\r
7275       ScreenToClient(hDlg, &pt);\r
7276       SetWindowPos(hInput, NULL, \r
7277         pt.x, pt.y, /* needs client coords */   \r
7278         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7279         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7280     }\r
7281     sizeX = newSizeX;\r
7282     sizeY = newSizeY;\r
7283     break;\r
7284 \r
7285   case WM_GETMINMAXINFO:\r
7286     /* Prevent resizing window too small */\r
7287     mmi = (MINMAXINFO *) lParam;\r
7288     mmi->ptMinTrackSize.x = 100;\r
7289     mmi->ptMinTrackSize.y = 100;\r
7290     break;\r
7291 \r
7292   /* [AS] Snapping */\r
7293   case WM_ENTERSIZEMOVE:\r
7294     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7295 \r
7296   case WM_SIZING:\r
7297     return OnSizing( &sd, hDlg, wParam, lParam );\r
7298 \r
7299   case WM_MOVING:\r
7300     return OnMoving( &sd, hDlg, wParam, lParam );\r
7301 \r
7302   case WM_EXITSIZEMOVE:\r
7303         UpdateICSWidth(hText);\r
7304     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7305   }\r
7306 \r
7307   return DefWindowProc(hDlg, message, wParam, lParam);\r
7308 }\r
7309 \r
7310 \r
7311 VOID\r
7312 ConsoleCreate()\r
7313 {\r
7314   HWND hCons;\r
7315   if (hwndConsole) return;\r
7316   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7317   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7318 }\r
7319 \r
7320 \r
7321 VOID\r
7322 ConsoleOutput(char* data, int length, int forceVisible)\r
7323 {\r
7324   HWND hText;\r
7325   int trim, exlen;\r
7326   char *p, *q;\r
7327   char buf[CO_MAX+1];\r
7328   POINT pEnd;\r
7329   RECT rect;\r
7330   static int delayLF = 0;\r
7331   CHARRANGE savesel, sel;\r
7332 \r
7333   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7334   p = data;\r
7335   q = buf;\r
7336   if (delayLF) {\r
7337     *q++ = '\r';\r
7338     *q++ = '\n';\r
7339     delayLF = 0;\r
7340   }\r
7341   while (length--) {\r
7342     if (*p == '\n') {\r
7343       if (*++p) {\r
7344         *q++ = '\r';\r
7345         *q++ = '\n';\r
7346       } else {\r
7347         delayLF = 1;\r
7348       }\r
7349     } else if (*p == '\007') {\r
7350        MyPlaySound(&sounds[(int)SoundBell]);\r
7351        p++;\r
7352     } else {\r
7353       *q++ = *p++;\r
7354     }\r
7355   }\r
7356   *q = NULLCHAR;\r
7357   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7358   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7359   /* Save current selection */\r
7360   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7361   exlen = GetWindowTextLength(hText);\r
7362   /* Find out whether current end of text is visible */\r
7363   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7364   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7365   /* Trim existing text if it's too long */\r
7366   if (exlen + (q - buf) > CO_MAX) {\r
7367     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7368     sel.cpMin = 0;\r
7369     sel.cpMax = trim;\r
7370     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7371     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7372     exlen -= trim;\r
7373     savesel.cpMin -= trim;\r
7374     savesel.cpMax -= trim;\r
7375     if (exlen < 0) exlen = 0;\r
7376     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7377     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7378   }\r
7379   /* Append the new text */\r
7380   sel.cpMin = exlen;\r
7381   sel.cpMax = exlen;\r
7382   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7383   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7384   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7385   if (forceVisible || exlen == 0 ||\r
7386       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7387        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7388     /* Scroll to make new end of text visible if old end of text\r
7389        was visible or new text is an echo of user typein */\r
7390     sel.cpMin = 9999999;\r
7391     sel.cpMax = 9999999;\r
7392     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7393     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7394     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7395     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7396   }\r
7397   if (savesel.cpMax == exlen || forceVisible) {\r
7398     /* Move insert point to new end of text if it was at the old\r
7399        end of text or if the new text is an echo of user typein */\r
7400     sel.cpMin = 9999999;\r
7401     sel.cpMax = 9999999;\r
7402     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7403   } else {\r
7404     /* Restore previous selection */\r
7405     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7406   }\r
7407   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7408 }\r
7409 \r
7410 /*---------*/\r
7411 \r
7412 \r
7413 void\r
7414 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7415 {\r
7416   char buf[100];\r
7417   char *str;\r
7418   COLORREF oldFg, oldBg;\r
7419   HFONT oldFont;\r
7420   RECT rect;\r
7421 \r
7422   if(copyNumber > 1)
7423     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7424 \r
7425   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7426   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7427   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7428 \r
7429   rect.left = x;\r
7430   rect.right = x + squareSize;\r
7431   rect.top  = y;\r
7432   rect.bottom = y + squareSize;\r
7433   str = buf;\r
7434 \r
7435   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7436                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7437              y, ETO_CLIPPED|ETO_OPAQUE,\r
7438              &rect, str, strlen(str), NULL);\r
7439 \r
7440   (void) SetTextColor(hdc, oldFg);\r
7441   (void) SetBkColor(hdc, oldBg);\r
7442   (void) SelectObject(hdc, oldFont);\r
7443 }\r
7444 \r
7445 void\r
7446 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7447               RECT *rect, char *color, char *flagFell)\r
7448 {\r
7449   char buf[100];\r
7450   char *str;\r
7451   COLORREF oldFg, oldBg;\r
7452   HFONT oldFont;\r
7453 \r
7454   if (appData.clockMode) {\r
7455     if (tinyLayout)\r
7456       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7457     else\r
7458       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7459     str = buf;\r
7460   } else {\r
7461     str = color;\r
7462   }\r
7463 \r
7464   if (highlight) {\r
7465     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7466     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7467   } else {\r
7468     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7469     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7470   }\r
7471   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7472 \r
7473   JAWS_SILENCE\r
7474 \r
7475   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7476              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7477              rect, str, strlen(str), NULL);\r
7478   if(logoHeight > 0 && appData.clockMode) {\r
7479       RECT r;\r
7480       str += strlen(color)+2;\r
7481       r.top = rect->top + logoHeight/2;\r
7482       r.left = rect->left;\r
7483       r.right = rect->right;\r
7484       r.bottom = rect->bottom;\r
7485       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7486                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7487                  &r, str, strlen(str), NULL);\r
7488   }\r
7489   (void) SetTextColor(hdc, oldFg);\r
7490   (void) SetBkColor(hdc, oldBg);\r
7491   (void) SelectObject(hdc, oldFont);\r
7492 }\r
7493 \r
7494 \r
7495 int\r
7496 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7497            OVERLAPPED *ovl)\r
7498 {\r
7499   int ok, err;\r
7500 \r
7501   /* [AS]  */\r
7502   if( count <= 0 ) {\r
7503     if (appData.debugMode) {\r
7504       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7505     }\r
7506 \r
7507     return ERROR_INVALID_USER_BUFFER;\r
7508   }\r
7509 \r
7510   ResetEvent(ovl->hEvent);\r
7511   ovl->Offset = ovl->OffsetHigh = 0;\r
7512   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7513   if (ok) {\r
7514     err = NO_ERROR;\r
7515   } else {\r
7516     err = GetLastError();\r
7517     if (err == ERROR_IO_PENDING) {\r
7518       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7519       if (ok)\r
7520         err = NO_ERROR;\r
7521       else\r
7522         err = GetLastError();\r
7523     }\r
7524   }\r
7525   return err;\r
7526 }\r
7527 \r
7528 int\r
7529 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7530             OVERLAPPED *ovl)\r
7531 {\r
7532   int ok, err;\r
7533 \r
7534   ResetEvent(ovl->hEvent);\r
7535   ovl->Offset = ovl->OffsetHigh = 0;\r
7536   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7537   if (ok) {\r
7538     err = NO_ERROR;\r
7539   } else {\r
7540     err = GetLastError();\r
7541     if (err == ERROR_IO_PENDING) {\r
7542       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7543       if (ok)\r
7544         err = NO_ERROR;\r
7545       else\r
7546         err = GetLastError();\r
7547     }\r
7548   }\r
7549   return err;\r
7550 }\r
7551 \r
7552 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7553 void CheckForInputBufferFull( InputSource * is )\r
7554 {\r
7555     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7556         /* Look for end of line */\r
7557         char * p = is->buf;\r
7558         \r
7559         while( p < is->next && *p != '\n' ) {\r
7560             p++;\r
7561         }\r
7562 \r
7563         if( p >= is->next ) {\r
7564             if (appData.debugMode) {\r
7565                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7566             }\r
7567 \r
7568             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7569             is->count = (DWORD) -1;\r
7570             is->next = is->buf;\r
7571         }\r
7572     }\r
7573 }\r
7574 \r
7575 DWORD\r
7576 InputThread(LPVOID arg)\r
7577 {\r
7578   InputSource *is;\r
7579   OVERLAPPED ovl;\r
7580 \r
7581   is = (InputSource *) arg;\r
7582   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7583   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7584   while (is->hThread != NULL) {\r
7585     is->error = DoReadFile(is->hFile, is->next,\r
7586                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7587                            &is->count, &ovl);\r
7588     if (is->error == NO_ERROR) {\r
7589       is->next += is->count;\r
7590     } else {\r
7591       if (is->error == ERROR_BROKEN_PIPE) {\r
7592         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7593         is->count = 0;\r
7594       } else {\r
7595         is->count = (DWORD) -1;\r
7596         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7597         break; \r
7598       }\r
7599     }\r
7600 \r
7601     CheckForInputBufferFull( is );\r
7602 \r
7603     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7604 \r
7605     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7606 \r
7607     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7608   }\r
7609 \r
7610   CloseHandle(ovl.hEvent);\r
7611   CloseHandle(is->hFile);\r
7612 \r
7613   if (appData.debugMode) {\r
7614     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7615   }\r
7616 \r
7617   return 0;\r
7618 }\r
7619 \r
7620 \r
7621 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7622 DWORD\r
7623 NonOvlInputThread(LPVOID arg)\r
7624 {\r
7625   InputSource *is;\r
7626   char *p, *q;\r
7627   int i;\r
7628   char prev;\r
7629 \r
7630   is = (InputSource *) arg;\r
7631   while (is->hThread != NULL) {\r
7632     is->error = ReadFile(is->hFile, is->next,\r
7633                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7634                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7635     if (is->error == NO_ERROR) {\r
7636       /* Change CRLF to LF */\r
7637       if (is->next > is->buf) {\r
7638         p = is->next - 1;\r
7639         i = is->count + 1;\r
7640       } else {\r
7641         p = is->next;\r
7642         i = is->count;\r
7643       }\r
7644       q = p;\r
7645       prev = NULLCHAR;\r
7646       while (i > 0) {\r
7647         if (prev == '\r' && *p == '\n') {\r
7648           *(q-1) = '\n';\r
7649           is->count--;\r
7650         } else { \r
7651           *q++ = *p;\r
7652         }\r
7653         prev = *p++;\r
7654         i--;\r
7655       }\r
7656       *q = NULLCHAR;\r
7657       is->next = q;\r
7658     } else {\r
7659       if (is->error == ERROR_BROKEN_PIPE) {\r
7660         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7661         is->count = 0; \r
7662       } else {\r
7663         is->count = (DWORD) -1;\r
7664       }\r
7665     }\r
7666 \r
7667     CheckForInputBufferFull( is );\r
7668 \r
7669     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7670 \r
7671     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7672 \r
7673     if (is->count < 0) break;  /* Quit on error */\r
7674   }\r
7675   CloseHandle(is->hFile);\r
7676   return 0;\r
7677 }\r
7678 \r
7679 DWORD\r
7680 SocketInputThread(LPVOID arg)\r
7681 {\r
7682   InputSource *is;\r
7683 \r
7684   is = (InputSource *) arg;\r
7685   while (is->hThread != NULL) {\r
7686     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7687     if ((int)is->count == SOCKET_ERROR) {\r
7688       is->count = (DWORD) -1;\r
7689       is->error = WSAGetLastError();\r
7690     } else {\r
7691       is->error = NO_ERROR;\r
7692       is->next += is->count;\r
7693       if (is->count == 0 && is->second == is) {\r
7694         /* End of file on stderr; quit with no message */\r
7695         break;\r
7696       }\r
7697     }\r
7698     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7699 \r
7700     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7701 \r
7702     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7703   }\r
7704   return 0;\r
7705 }\r
7706 \r
7707 VOID\r
7708 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7709 {\r
7710   InputSource *is;\r
7711 \r
7712   is = (InputSource *) lParam;\r
7713   if (is->lineByLine) {\r
7714     /* Feed in lines one by one */\r
7715     char *p = is->buf;\r
7716     char *q = p;\r
7717     while (q < is->next) {\r
7718       if (*q++ == '\n') {\r
7719         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7720         p = q;\r
7721       }\r
7722     }\r
7723     \r
7724     /* Move any partial line to the start of the buffer */\r
7725     q = is->buf;\r
7726     while (p < is->next) {\r
7727       *q++ = *p++;\r
7728     }\r
7729     is->next = q;\r
7730 \r
7731     if (is->error != NO_ERROR || is->count == 0) {\r
7732       /* Notify backend of the error.  Note: If there was a partial\r
7733          line at the end, it is not flushed through. */\r
7734       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7735     }\r
7736   } else {\r
7737     /* Feed in the whole chunk of input at once */\r
7738     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7739     is->next = is->buf;\r
7740   }\r
7741 }\r
7742 \r
7743 /*---------------------------------------------------------------------------*\\r
7744  *\r
7745  *  Menu enables. Used when setting various modes.\r
7746  *\r
7747 \*---------------------------------------------------------------------------*/\r
7748 \r
7749 typedef struct {\r
7750   int item;\r
7751   int flags;\r
7752 } Enables;\r
7753 \r
7754 VOID\r
7755 GreyRevert(Boolean grey)\r
7756 { // [HGM] vari: for retracting variations in local mode\r
7757   HMENU hmenu = GetMenu(hwndMain);\r
7758   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7759   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7760 }\r
7761 \r
7762 VOID\r
7763 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7764 {\r
7765   while (enab->item > 0) {\r
7766     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7767     enab++;\r
7768   }\r
7769 }\r
7770 \r
7771 Enables gnuEnables[] = {\r
7772   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7785 \r
7786   // Needed to switch from ncp to GNU mode on Engine Load\r
7787   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7788   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7789   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7790   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7791   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7792   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7793   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7794   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7795   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7796   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7797   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7798   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7799   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7800   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7801   { -1, -1 }\r
7802 };\r
7803 \r
7804 Enables icsEnables[] = {\r
7805   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7808   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7809   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7810   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7811   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7812   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7813   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7820   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7824   { -1, -1 }\r
7825 };\r
7826 \r
7827 #if ZIPPY\r
7828 Enables zippyEnables[] = {\r
7829   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7830   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7831   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7832   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7833   { -1, -1 }\r
7834 };\r
7835 #endif\r
7836 \r
7837 Enables ncpEnables[] = {\r
7838   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7847   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7848   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7860   { -1, -1 }\r
7861 };\r
7862 \r
7863 Enables trainingOnEnables[] = {\r
7864   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7867   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7868   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7869   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7870   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7871   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7873   { -1, -1 }\r
7874 };\r
7875 \r
7876 Enables trainingOffEnables[] = {\r
7877   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7881   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7882   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7883   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7884   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7885   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7886   { -1, -1 }\r
7887 };\r
7888 \r
7889 /* These modify either ncpEnables or gnuEnables */\r
7890 Enables cmailEnables[] = {\r
7891   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7892   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7893   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7894   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7896   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7898   { -1, -1 }\r
7899 };\r
7900 \r
7901 Enables machineThinkingEnables[] = {\r
7902   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7913   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7915 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7918   { -1, -1 }\r
7919 };\r
7920 \r
7921 Enables userThinkingEnables[] = {\r
7922   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7928   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7929   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7930   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7931   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7932   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7934   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7935 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7936   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7937   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7938   { -1, -1 }\r
7939 };\r
7940 \r
7941 /*---------------------------------------------------------------------------*\\r
7942  *\r
7943  *  Front-end interface functions exported by XBoard.\r
7944  *  Functions appear in same order as prototypes in frontend.h.\r
7945  * \r
7946 \*---------------------------------------------------------------------------*/\r
7947 VOID\r
7948 CheckMark(UINT item, int state)\r
7949 {\r
7950     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7951 }\r
7952 \r
7953 VOID\r
7954 ModeHighlight()\r
7955 {\r
7956   static UINT prevChecked = 0;\r
7957   static int prevPausing = 0;\r
7958   UINT nowChecked;\r
7959 \r
7960   if (pausing != prevPausing) {\r
7961     prevPausing = pausing;\r
7962     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7963                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7964     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7965   }\r
7966 \r
7967   switch (gameMode) {\r
7968   case BeginningOfGame:\r
7969     if (appData.icsActive)\r
7970       nowChecked = IDM_IcsClient;\r
7971     else if (appData.noChessProgram)\r
7972       nowChecked = IDM_EditGame;\r
7973     else\r
7974       nowChecked = IDM_MachineBlack;\r
7975     break;\r
7976   case MachinePlaysBlack:\r
7977     nowChecked = IDM_MachineBlack;\r
7978     break;\r
7979   case MachinePlaysWhite:\r
7980     nowChecked = IDM_MachineWhite;\r
7981     break;\r
7982   case TwoMachinesPlay:\r
7983     nowChecked = IDM_TwoMachines;\r
7984     break;\r
7985   case AnalyzeMode:\r
7986     nowChecked = IDM_AnalysisMode;\r
7987     break;\r
7988   case AnalyzeFile:\r
7989     nowChecked = IDM_AnalyzeFile;\r
7990     break;\r
7991   case EditGame:\r
7992     nowChecked = IDM_EditGame;\r
7993     break;\r
7994   case PlayFromGameFile:\r
7995     nowChecked = IDM_LoadGame;\r
7996     break;\r
7997   case EditPosition:\r
7998     nowChecked = IDM_EditPosition;\r
7999     break;\r
8000   case Training:\r
8001     nowChecked = IDM_Training;\r
8002     break;\r
8003   case IcsPlayingWhite:\r
8004   case IcsPlayingBlack:\r
8005   case IcsObserving:\r
8006   case IcsIdle:\r
8007     nowChecked = IDM_IcsClient;\r
8008     break;\r
8009   default:\r
8010   case EndOfGame:\r
8011     nowChecked = 0;\r
8012     break;\r
8013   }\r
8014   CheckMark(prevChecked, MF_UNCHECKED);\r
8015   CheckMark(nowChecked, MF_CHECKED);\r
8016   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8017 \r
8018   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8019     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8020                           MF_BYCOMMAND|MF_ENABLED);\r
8021   } else {\r
8022     (void) EnableMenuItem(GetMenu(hwndMain), \r
8023                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8024   }\r
8025 \r
8026   prevChecked = nowChecked;\r
8027 \r
8028   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8029   if (appData.icsActive) {\r
8030        if (appData.icsEngineAnalyze) {\r
8031                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8032        } else {\r
8033                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8034        }\r
8035   }\r
8036   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8037 }\r
8038 \r
8039 VOID\r
8040 SetICSMode()\r
8041 {\r
8042   HMENU hmenu = GetMenu(hwndMain);\r
8043   SetMenuEnables(hmenu, icsEnables);\r
8044   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8045     MF_BYCOMMAND|MF_ENABLED);\r
8046 #if ZIPPY\r
8047   if (appData.zippyPlay) {\r
8048     SetMenuEnables(hmenu, zippyEnables);\r
8049     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8050          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8051           MF_BYCOMMAND|MF_ENABLED);\r
8052   }\r
8053 #endif\r
8054 }\r
8055 \r
8056 VOID\r
8057 SetGNUMode()\r
8058 {\r
8059   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8060 }\r
8061 \r
8062 VOID\r
8063 SetNCPMode()\r
8064 {\r
8065   HMENU hmenu = GetMenu(hwndMain);\r
8066   SetMenuEnables(hmenu, ncpEnables);\r
8067     DrawMenuBar(hwndMain);\r
8068 }\r
8069 \r
8070 VOID\r
8071 SetCmailMode()\r
8072 {\r
8073   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8074 }\r
8075 \r
8076 VOID \r
8077 SetTrainingModeOn()\r
8078 {\r
8079   int i;\r
8080   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8081   for (i = 0; i < N_BUTTONS; i++) {\r
8082     if (buttonDesc[i].hwnd != NULL)\r
8083       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8084   }\r
8085   CommentPopDown();\r
8086 }\r
8087 \r
8088 VOID SetTrainingModeOff()\r
8089 {\r
8090   int i;\r
8091   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8092   for (i = 0; i < N_BUTTONS; i++) {\r
8093     if (buttonDesc[i].hwnd != NULL)\r
8094       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8095   }\r
8096 }\r
8097 \r
8098 \r
8099 VOID\r
8100 SetUserThinkingEnables()\r
8101 {\r
8102   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8103 }\r
8104 \r
8105 VOID\r
8106 SetMachineThinkingEnables()\r
8107 {\r
8108   HMENU hMenu = GetMenu(hwndMain);\r
8109   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8110 \r
8111   SetMenuEnables(hMenu, machineThinkingEnables);\r
8112 \r
8113   if (gameMode == MachinePlaysBlack) {\r
8114     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8115   } else if (gameMode == MachinePlaysWhite) {\r
8116     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8117   } else if (gameMode == TwoMachinesPlay) {\r
8118     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8119   }\r
8120 }\r
8121 \r
8122 \r
8123 VOID\r
8124 DisplayTitle(char *str)\r
8125 {\r
8126   char title[MSG_SIZ], *host;\r
8127   if (str[0] != NULLCHAR) {\r
8128     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8129   } else if (appData.icsActive) {\r
8130     if (appData.icsCommPort[0] != NULLCHAR)\r
8131       host = "ICS";\r
8132     else \r
8133       host = appData.icsHost;\r
8134       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8135   } else if (appData.noChessProgram) {\r
8136     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8137   } else {\r
8138     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8139     strcat(title, ": ");\r
8140     strcat(title, first.tidy);\r
8141   }\r
8142   SetWindowText(hwndMain, title);\r
8143 }\r
8144 \r
8145 \r
8146 VOID\r
8147 DisplayMessage(char *str1, char *str2)\r
8148 {\r
8149   HDC hdc;\r
8150   HFONT oldFont;\r
8151   int remain = MESSAGE_TEXT_MAX - 1;\r
8152   int len;\r
8153 \r
8154   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8155   messageText[0] = NULLCHAR;\r
8156   if (*str1) {\r
8157     len = strlen(str1);\r
8158     if (len > remain) len = remain;\r
8159     strncpy(messageText, str1, len);\r
8160     messageText[len] = NULLCHAR;\r
8161     remain -= len;\r
8162   }\r
8163   if (*str2 && remain >= 2) {\r
8164     if (*str1) {\r
8165       strcat(messageText, "  ");\r
8166       remain -= 2;\r
8167     }\r
8168     len = strlen(str2);\r
8169     if (len > remain) len = remain;\r
8170     strncat(messageText, str2, len);\r
8171   }\r
8172   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8173   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8174 \r
8175   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8176 \r
8177   SAYMACHINEMOVE();\r
8178 \r
8179   hdc = GetDC(hwndMain);\r
8180   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8181   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8182              &messageRect, messageText, strlen(messageText), NULL);\r
8183   (void) SelectObject(hdc, oldFont);\r
8184   (void) ReleaseDC(hwndMain, hdc);\r
8185 }\r
8186 \r
8187 VOID\r
8188 DisplayError(char *str, int error)\r
8189 {\r
8190   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8191   int len;\r
8192 \r
8193   if (error == 0) {\r
8194     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8195   } else {\r
8196     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8197                         NULL, error, LANG_NEUTRAL,\r
8198                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8199     if (len > 0) {\r
8200       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8201     } else {\r
8202       ErrorMap *em = errmap;\r
8203       while (em->err != 0 && em->err != error) em++;\r
8204       if (em->err != 0) {\r
8205         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8206       } else {\r
8207         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8208       }\r
8209     }\r
8210   }\r
8211   \r
8212   ErrorPopUp(_("Error"), buf);\r
8213 }\r
8214 \r
8215 \r
8216 VOID\r
8217 DisplayMoveError(char *str)\r
8218 {\r
8219   fromX = fromY = -1;\r
8220   ClearHighlights();\r
8221   DrawPosition(FALSE, NULL);\r
8222   if (appData.popupMoveErrors) {\r
8223     ErrorPopUp(_("Error"), str);\r
8224   } else {\r
8225     DisplayMessage(str, "");\r
8226     moveErrorMessageUp = TRUE;\r
8227   }\r
8228 }\r
8229 \r
8230 VOID\r
8231 DisplayFatalError(char *str, int error, int exitStatus)\r
8232 {\r
8233   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8234   int len;\r
8235   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8236 \r
8237   if (error != 0) {\r
8238     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8239                         NULL, error, LANG_NEUTRAL,\r
8240                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8241     if (len > 0) {\r
8242       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8243     } else {\r
8244       ErrorMap *em = errmap;\r
8245       while (em->err != 0 && em->err != error) em++;\r
8246       if (em->err != 0) {\r
8247         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8248       } else {\r
8249         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8250       }\r
8251     }\r
8252     str = buf;\r
8253   }\r
8254   if (appData.debugMode) {\r
8255     fprintf(debugFP, "%s: %s\n", label, str);\r
8256   }\r
8257   if (appData.popupExitMessage) {\r
8258     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8259                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8260   }\r
8261   ExitEvent(exitStatus);\r
8262 }\r
8263 \r
8264 \r
8265 VOID\r
8266 DisplayInformation(char *str)\r
8267 {\r
8268   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8269 }\r
8270 \r
8271 \r
8272 VOID\r
8273 DisplayNote(char *str)\r
8274 {\r
8275   ErrorPopUp(_("Note"), str);\r
8276 }\r
8277 \r
8278 \r
8279 typedef struct {\r
8280   char *title, *question, *replyPrefix;\r
8281   ProcRef pr;\r
8282 } QuestionParams;\r
8283 \r
8284 LRESULT CALLBACK\r
8285 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8286 {\r
8287   static QuestionParams *qp;\r
8288   char reply[MSG_SIZ];\r
8289   int len, err;\r
8290 \r
8291   switch (message) {\r
8292   case WM_INITDIALOG:\r
8293     qp = (QuestionParams *) lParam;\r
8294     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8295     Translate(hDlg, DLG_Question);\r
8296     SetWindowText(hDlg, qp->title);\r
8297     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8298     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8299     return FALSE;\r
8300 \r
8301   case WM_COMMAND:\r
8302     switch (LOWORD(wParam)) {\r
8303     case IDOK:\r
8304       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8305       if (*reply) strcat(reply, " ");\r
8306       len = strlen(reply);\r
8307       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8308       strcat(reply, "\n");\r
8309       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8310       EndDialog(hDlg, TRUE);\r
8311       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8312       return TRUE;\r
8313     case IDCANCEL:\r
8314       EndDialog(hDlg, FALSE);\r
8315       return TRUE;\r
8316     default:\r
8317       break;\r
8318     }\r
8319     break;\r
8320   }\r
8321   return FALSE;\r
8322 }\r
8323 \r
8324 VOID\r
8325 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8326 {\r
8327     QuestionParams qp;\r
8328     FARPROC lpProc;\r
8329     \r
8330     qp.title = title;\r
8331     qp.question = question;\r
8332     qp.replyPrefix = replyPrefix;\r
8333     qp.pr = pr;\r
8334     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8335     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8336       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8337     FreeProcInstance(lpProc);\r
8338 }\r
8339 \r
8340 /* [AS] Pick FRC position */\r
8341 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8342 {\r
8343     static int * lpIndexFRC;\r
8344     BOOL index_is_ok;\r
8345     char buf[16];\r
8346 \r
8347     switch( message )\r
8348     {\r
8349     case WM_INITDIALOG:\r
8350         lpIndexFRC = (int *) lParam;\r
8351 \r
8352         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8353         Translate(hDlg, DLG_NewGameFRC);\r
8354 \r
8355         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8356         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8357         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8358         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8359 \r
8360         break;\r
8361 \r
8362     case WM_COMMAND:\r
8363         switch( LOWORD(wParam) ) {\r
8364         case IDOK:\r
8365             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8366             EndDialog( hDlg, 0 );\r
8367             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8368             return TRUE;\r
8369         case IDCANCEL:\r
8370             EndDialog( hDlg, 1 );   \r
8371             return TRUE;\r
8372         case IDC_NFG_Edit:\r
8373             if( HIWORD(wParam) == EN_CHANGE ) {\r
8374                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8375 \r
8376                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8377             }\r
8378             return TRUE;\r
8379         case IDC_NFG_Random:\r
8380           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8381             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8382             return TRUE;\r
8383         }\r
8384 \r
8385         break;\r
8386     }\r
8387 \r
8388     return FALSE;\r
8389 }\r
8390 \r
8391 int NewGameFRC()\r
8392 {\r
8393     int result;\r
8394     int index = appData.defaultFrcPosition;\r
8395     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8396 \r
8397     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8398 \r
8399     if( result == 0 ) {\r
8400         appData.defaultFrcPosition = index;\r
8401     }\r
8402 \r
8403     return result;\r
8404 }\r
8405 \r
8406 /* [AS] Game list options. Refactored by HGM */\r
8407 \r
8408 HWND gameListOptionsDialog;\r
8409 \r
8410 // low-level front-end: clear text edit / list widget\r
8411 void\r
8412 GLT_ClearList()\r
8413 {\r
8414     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8415 }\r
8416 \r
8417 // low-level front-end: clear text edit / list widget\r
8418 void\r
8419 GLT_DeSelectList()\r
8420 {\r
8421     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8422 }\r
8423 \r
8424 // low-level front-end: append line to text edit / list widget\r
8425 void\r
8426 GLT_AddToList( char *name )\r
8427 {\r
8428     if( name != 0 ) {\r
8429             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8430     }\r
8431 }\r
8432 \r
8433 // low-level front-end: get line from text edit / list widget\r
8434 Boolean\r
8435 GLT_GetFromList( int index, char *name )\r
8436 {\r
8437     if( name != 0 ) {\r
8438             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8439                 return TRUE;\r
8440     }\r
8441     return FALSE;\r
8442 }\r
8443 \r
8444 void GLT_MoveSelection( HWND hDlg, int delta )\r
8445 {\r
8446     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8447     int idx2 = idx1 + delta;\r
8448     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8449 \r
8450     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8451         char buf[128];\r
8452 \r
8453         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8454         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8455         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8456         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8457     }\r
8458 }\r
8459 \r
8460 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8461 {\r
8462     switch( message )\r
8463     {\r
8464     case WM_INITDIALOG:\r
8465         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8466         \r
8467         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8468         Translate(hDlg, DLG_GameListOptions);\r
8469 \r
8470         /* Initialize list */\r
8471         GLT_TagsToList( lpUserGLT );\r
8472 \r
8473         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8474 \r
8475         break;\r
8476 \r
8477     case WM_COMMAND:\r
8478         switch( LOWORD(wParam) ) {\r
8479         case IDOK:\r
8480             GLT_ParseList();\r
8481             EndDialog( hDlg, 0 );\r
8482             return TRUE;\r
8483         case IDCANCEL:\r
8484             EndDialog( hDlg, 1 );\r
8485             return TRUE;\r
8486 \r
8487         case IDC_GLT_Default:\r
8488             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8489             return TRUE;\r
8490 \r
8491         case IDC_GLT_Restore:\r
8492             GLT_TagsToList( appData.gameListTags );\r
8493             return TRUE;\r
8494 \r
8495         case IDC_GLT_Up:\r
8496             GLT_MoveSelection( hDlg, -1 );\r
8497             return TRUE;\r
8498 \r
8499         case IDC_GLT_Down:\r
8500             GLT_MoveSelection( hDlg, +1 );\r
8501             return TRUE;\r
8502         }\r
8503 \r
8504         break;\r
8505     }\r
8506 \r
8507     return FALSE;\r
8508 }\r
8509 \r
8510 int GameListOptions()\r
8511 {\r
8512     int result;\r
8513     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8514 \r
8515       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8516 \r
8517     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8518 \r
8519     if( result == 0 ) {\r
8520         /* [AS] Memory leak here! */\r
8521         appData.gameListTags = strdup( lpUserGLT ); \r
8522     }\r
8523 \r
8524     return result;\r
8525 }\r
8526 \r
8527 VOID\r
8528 DisplayIcsInteractionTitle(char *str)\r
8529 {\r
8530   char consoleTitle[MSG_SIZ];\r
8531 \r
8532     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8533   SetWindowText(hwndConsole, consoleTitle);\r
8534 }\r
8535 \r
8536 void\r
8537 DrawPosition(int fullRedraw, Board board)\r
8538 {\r
8539   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8540 }\r
8541 \r
8542 void NotifyFrontendLogin()\r
8543 {\r
8544         if (hwndConsole)\r
8545                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8546 }\r
8547 \r
8548 VOID\r
8549 ResetFrontEnd()\r
8550 {\r
8551   fromX = fromY = -1;\r
8552   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8553     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8554     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8555     dragInfo.lastpos = dragInfo.pos;\r
8556     dragInfo.start.x = dragInfo.start.y = -1;\r
8557     dragInfo.from = dragInfo.start;\r
8558     ReleaseCapture();\r
8559     DrawPosition(TRUE, NULL);\r
8560   }\r
8561   TagsPopDown();\r
8562 }\r
8563 \r
8564 \r
8565 VOID\r
8566 CommentPopUp(char *title, char *str)\r
8567 {\r
8568   HWND hwnd = GetActiveWindow();\r
8569   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8570   SAY(str);\r
8571   SetActiveWindow(hwnd);\r
8572 }\r
8573 \r
8574 VOID\r
8575 CommentPopDown(void)\r
8576 {\r
8577   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8578   if (commentDialog) {\r
8579     ShowWindow(commentDialog, SW_HIDE);\r
8580   }\r
8581   commentUp = FALSE;\r
8582 }\r
8583 \r
8584 VOID\r
8585 EditCommentPopUp(int index, char *title, char *str)\r
8586 {\r
8587   EitherCommentPopUp(index, title, str, TRUE);\r
8588 }\r
8589 \r
8590 \r
8591 VOID\r
8592 RingBell()\r
8593 {\r
8594   MyPlaySound(&sounds[(int)SoundMove]);\r
8595 }\r
8596 \r
8597 VOID PlayIcsWinSound()\r
8598 {\r
8599   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8600 }\r
8601 \r
8602 VOID PlayIcsLossSound()\r
8603 {\r
8604   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8605 }\r
8606 \r
8607 VOID PlayIcsDrawSound()\r
8608 {\r
8609   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8610 }\r
8611 \r
8612 VOID PlayIcsUnfinishedSound()\r
8613 {\r
8614   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8615 }\r
8616 \r
8617 VOID\r
8618 PlayAlarmSound()\r
8619 {\r
8620   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8621 }\r
8622 \r
8623 VOID\r
8624 PlayTellSound()\r
8625 {\r
8626   MyPlaySound(&textAttribs[ColorTell].sound);\r
8627 }\r
8628 \r
8629 \r
8630 VOID\r
8631 EchoOn()\r
8632 {\r
8633   HWND hInput;\r
8634   consoleEcho = TRUE;\r
8635   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8636   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8637   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8638 }\r
8639 \r
8640 \r
8641 VOID\r
8642 EchoOff()\r
8643 {\r
8644   CHARFORMAT cf;\r
8645   HWND hInput;\r
8646   consoleEcho = FALSE;\r
8647   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8648   /* This works OK: set text and background both to the same color */\r
8649   cf = consoleCF;\r
8650   cf.crTextColor = COLOR_ECHOOFF;\r
8651   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8652   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8653 }\r
8654 \r
8655 /* No Raw()...? */\r
8656 \r
8657 void Colorize(ColorClass cc, int continuation)\r
8658 {\r
8659   currentColorClass = cc;\r
8660   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8661   consoleCF.crTextColor = textAttribs[cc].color;\r
8662   consoleCF.dwEffects = textAttribs[cc].effects;\r
8663   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8664 }\r
8665 \r
8666 char *\r
8667 UserName()\r
8668 {\r
8669   static char buf[MSG_SIZ];\r
8670   DWORD bufsiz = MSG_SIZ;\r
8671 \r
8672   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8673         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8674   }\r
8675   if (!GetUserName(buf, &bufsiz)) {\r
8676     /*DisplayError("Error getting user name", GetLastError());*/\r
8677     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8678   }\r
8679   return buf;\r
8680 }\r
8681 \r
8682 char *\r
8683 HostName()\r
8684 {\r
8685   static char buf[MSG_SIZ];\r
8686   DWORD bufsiz = MSG_SIZ;\r
8687 \r
8688   if (!GetComputerName(buf, &bufsiz)) {\r
8689     /*DisplayError("Error getting host name", GetLastError());*/\r
8690     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8691   }\r
8692   return buf;\r
8693 }\r
8694 \r
8695 \r
8696 int\r
8697 ClockTimerRunning()\r
8698 {\r
8699   return clockTimerEvent != 0;\r
8700 }\r
8701 \r
8702 int\r
8703 StopClockTimer()\r
8704 {\r
8705   if (clockTimerEvent == 0) return FALSE;\r
8706   KillTimer(hwndMain, clockTimerEvent);\r
8707   clockTimerEvent = 0;\r
8708   return TRUE;\r
8709 }\r
8710 \r
8711 void\r
8712 StartClockTimer(long millisec)\r
8713 {\r
8714   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8715                              (UINT) millisec, NULL);\r
8716 }\r
8717 \r
8718 void\r
8719 DisplayWhiteClock(long timeRemaining, int highlight)\r
8720 {\r
8721   HDC hdc;\r
8722   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8723 \r
8724   if(appData.noGUI) return;\r
8725   hdc = GetDC(hwndMain);\r
8726   if (!IsIconic(hwndMain)) {\r
8727     DisplayAClock(hdc, timeRemaining, highlight, \r
8728                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8729   }\r
8730   if (highlight && iconCurrent == iconBlack) {\r
8731     iconCurrent = iconWhite;\r
8732     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8733     if (IsIconic(hwndMain)) {\r
8734       DrawIcon(hdc, 2, 2, iconCurrent);\r
8735     }\r
8736   }\r
8737   (void) ReleaseDC(hwndMain, hdc);\r
8738   if (hwndConsole)\r
8739     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8740 }\r
8741 \r
8742 void\r
8743 DisplayBlackClock(long timeRemaining, int highlight)\r
8744 {\r
8745   HDC hdc;\r
8746   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8747 \r
8748   if(appData.noGUI) return;\r
8749   hdc = GetDC(hwndMain);\r
8750   if (!IsIconic(hwndMain)) {\r
8751     DisplayAClock(hdc, timeRemaining, highlight, \r
8752                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8753   }\r
8754   if (highlight && iconCurrent == iconWhite) {\r
8755     iconCurrent = iconBlack;\r
8756     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8757     if (IsIconic(hwndMain)) {\r
8758       DrawIcon(hdc, 2, 2, iconCurrent);\r
8759     }\r
8760   }\r
8761   (void) ReleaseDC(hwndMain, hdc);\r
8762   if (hwndConsole)\r
8763     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8764 }\r
8765 \r
8766 \r
8767 int\r
8768 LoadGameTimerRunning()\r
8769 {\r
8770   return loadGameTimerEvent != 0;\r
8771 }\r
8772 \r
8773 int\r
8774 StopLoadGameTimer()\r
8775 {\r
8776   if (loadGameTimerEvent == 0) return FALSE;\r
8777   KillTimer(hwndMain, loadGameTimerEvent);\r
8778   loadGameTimerEvent = 0;\r
8779   return TRUE;\r
8780 }\r
8781 \r
8782 void\r
8783 StartLoadGameTimer(long millisec)\r
8784 {\r
8785   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8786                                 (UINT) millisec, NULL);\r
8787 }\r
8788 \r
8789 void\r
8790 AutoSaveGame()\r
8791 {\r
8792   char *defName;\r
8793   FILE *f;\r
8794   char fileTitle[MSG_SIZ];\r
8795 \r
8796   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8797   f = OpenFileDialog(hwndMain, "a", defName,\r
8798                      appData.oldSaveStyle ? "gam" : "pgn",\r
8799                      GAME_FILT, \r
8800                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8801   if (f != NULL) {\r
8802     SaveGame(f, 0, "");\r
8803     fclose(f);\r
8804   }\r
8805 }\r
8806 \r
8807 \r
8808 void\r
8809 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8810 {\r
8811   if (delayedTimerEvent != 0) {\r
8812     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8813       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8814     }\r
8815     KillTimer(hwndMain, delayedTimerEvent);\r
8816     delayedTimerEvent = 0;\r
8817     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8818     delayedTimerCallback();\r
8819   }\r
8820   delayedTimerCallback = cb;\r
8821   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8822                                 (UINT) millisec, NULL);\r
8823 }\r
8824 \r
8825 DelayedEventCallback\r
8826 GetDelayedEvent()\r
8827 {\r
8828   if (delayedTimerEvent) {\r
8829     return delayedTimerCallback;\r
8830   } else {\r
8831     return NULL;\r
8832   }\r
8833 }\r
8834 \r
8835 void\r
8836 CancelDelayedEvent()\r
8837 {\r
8838   if (delayedTimerEvent) {\r
8839     KillTimer(hwndMain, delayedTimerEvent);\r
8840     delayedTimerEvent = 0;\r
8841   }\r
8842 }\r
8843 \r
8844 DWORD GetWin32Priority(int nice)\r
8845 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8846 /*\r
8847 REALTIME_PRIORITY_CLASS     0x00000100\r
8848 HIGH_PRIORITY_CLASS         0x00000080\r
8849 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8850 NORMAL_PRIORITY_CLASS       0x00000020\r
8851 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8852 IDLE_PRIORITY_CLASS         0x00000040\r
8853 */\r
8854         if (nice < -15) return 0x00000080;\r
8855         if (nice < 0)   return 0x00008000;\r
8856         if (nice == 0)  return 0x00000020;\r
8857         if (nice < 15)  return 0x00004000;\r
8858         return 0x00000040;\r
8859 }\r
8860 \r
8861 void RunCommand(char *cmdLine)\r
8862 {\r
8863   /* Now create the child process. */\r
8864   STARTUPINFO siStartInfo;\r
8865   PROCESS_INFORMATION piProcInfo;\r
8866 \r
8867   siStartInfo.cb = sizeof(STARTUPINFO);\r
8868   siStartInfo.lpReserved = NULL;\r
8869   siStartInfo.lpDesktop = NULL;\r
8870   siStartInfo.lpTitle = NULL;\r
8871   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8872   siStartInfo.cbReserved2 = 0;\r
8873   siStartInfo.lpReserved2 = NULL;\r
8874   siStartInfo.hStdInput = NULL;\r
8875   siStartInfo.hStdOutput = NULL;\r
8876   siStartInfo.hStdError = NULL;\r
8877 \r
8878   CreateProcess(NULL,\r
8879                 cmdLine,           /* command line */\r
8880                 NULL,      /* process security attributes */\r
8881                 NULL,      /* primary thread security attrs */\r
8882                 TRUE,      /* handles are inherited */\r
8883                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8884                 NULL,      /* use parent's environment */\r
8885                 NULL,\r
8886                 &siStartInfo, /* STARTUPINFO pointer */\r
8887                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8888 \r
8889   CloseHandle(piProcInfo.hThread);\r
8890 }\r
8891 \r
8892 /* Start a child process running the given program.\r
8893    The process's standard output can be read from "from", and its\r
8894    standard input can be written to "to".\r
8895    Exit with fatal error if anything goes wrong.\r
8896    Returns an opaque pointer that can be used to destroy the process\r
8897    later.\r
8898 */\r
8899 int\r
8900 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8901 {\r
8902 #define BUFSIZE 4096\r
8903 \r
8904   HANDLE hChildStdinRd, hChildStdinWr,\r
8905     hChildStdoutRd, hChildStdoutWr;\r
8906   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8907   SECURITY_ATTRIBUTES saAttr;\r
8908   BOOL fSuccess;\r
8909   PROCESS_INFORMATION piProcInfo;\r
8910   STARTUPINFO siStartInfo;\r
8911   ChildProc *cp;\r
8912   char buf[MSG_SIZ];\r
8913   DWORD err;\r
8914 \r
8915   if (appData.debugMode) {\r
8916     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8917   }\r
8918 \r
8919   *pr = NoProc;\r
8920 \r
8921   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8922   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8923   saAttr.bInheritHandle = TRUE;\r
8924   saAttr.lpSecurityDescriptor = NULL;\r
8925 \r
8926   /*\r
8927    * The steps for redirecting child's STDOUT:\r
8928    *     1. Create anonymous pipe to be STDOUT for child.\r
8929    *     2. Create a noninheritable duplicate of read handle,\r
8930    *         and close the inheritable read handle.\r
8931    */\r
8932 \r
8933   /* Create a pipe for the child's STDOUT. */\r
8934   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8935     return GetLastError();\r
8936   }\r
8937 \r
8938   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8939   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8940                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8941                              FALSE,     /* not inherited */\r
8942                              DUPLICATE_SAME_ACCESS);\r
8943   if (! fSuccess) {\r
8944     return GetLastError();\r
8945   }\r
8946   CloseHandle(hChildStdoutRd);\r
8947 \r
8948   /*\r
8949    * The steps for redirecting child's STDIN:\r
8950    *     1. Create anonymous pipe to be STDIN for child.\r
8951    *     2. Create a noninheritable duplicate of write handle,\r
8952    *         and close the inheritable write handle.\r
8953    */\r
8954 \r
8955   /* Create a pipe for the child's STDIN. */\r
8956   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8957     return GetLastError();\r
8958   }\r
8959 \r
8960   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8961   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8962                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8963                              FALSE,     /* not inherited */\r
8964                              DUPLICATE_SAME_ACCESS);\r
8965   if (! fSuccess) {\r
8966     return GetLastError();\r
8967   }\r
8968   CloseHandle(hChildStdinWr);\r
8969 \r
8970   /* Arrange to (1) look in dir for the child .exe file, and\r
8971    * (2) have dir be the child's working directory.  Interpret\r
8972    * dir relative to the directory WinBoard loaded from. */\r
8973   GetCurrentDirectory(MSG_SIZ, buf);\r
8974   SetCurrentDirectory(installDir);\r
8975   SetCurrentDirectory(dir);\r
8976 \r
8977   /* Now create the child process. */\r
8978 \r
8979   siStartInfo.cb = sizeof(STARTUPINFO);\r
8980   siStartInfo.lpReserved = NULL;\r
8981   siStartInfo.lpDesktop = NULL;\r
8982   siStartInfo.lpTitle = NULL;\r
8983   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8984   siStartInfo.cbReserved2 = 0;\r
8985   siStartInfo.lpReserved2 = NULL;\r
8986   siStartInfo.hStdInput = hChildStdinRd;\r
8987   siStartInfo.hStdOutput = hChildStdoutWr;\r
8988   siStartInfo.hStdError = hChildStdoutWr;\r
8989 \r
8990   fSuccess = CreateProcess(NULL,\r
8991                            cmdLine,        /* command line */\r
8992                            NULL,           /* process security attributes */\r
8993                            NULL,           /* primary thread security attrs */\r
8994                            TRUE,           /* handles are inherited */\r
8995                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8996                            NULL,           /* use parent's environment */\r
8997                            NULL,\r
8998                            &siStartInfo, /* STARTUPINFO pointer */\r
8999                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9000 \r
9001   err = GetLastError();\r
9002   SetCurrentDirectory(buf); /* return to prev directory */\r
9003   if (! fSuccess) {\r
9004     return err;\r
9005   }\r
9006 \r
9007   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9008     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9009     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9010   }\r
9011 \r
9012   /* Close the handles we don't need in the parent */\r
9013   CloseHandle(piProcInfo.hThread);\r
9014   CloseHandle(hChildStdinRd);\r
9015   CloseHandle(hChildStdoutWr);\r
9016 \r
9017   /* Prepare return value */\r
9018   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9019   cp->kind = CPReal;\r
9020   cp->hProcess = piProcInfo.hProcess;\r
9021   cp->pid = piProcInfo.dwProcessId;\r
9022   cp->hFrom = hChildStdoutRdDup;\r
9023   cp->hTo = hChildStdinWrDup;\r
9024 \r
9025   *pr = (void *) cp;\r
9026 \r
9027   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9028      2000 where engines sometimes don't see the initial command(s)\r
9029      from WinBoard and hang.  I don't understand how that can happen,\r
9030      but the Sleep is harmless, so I've put it in.  Others have also\r
9031      reported what may be the same problem, so hopefully this will fix\r
9032      it for them too.  */\r
9033   Sleep(500);\r
9034 \r
9035   return NO_ERROR;\r
9036 }\r
9037 \r
9038 \r
9039 void\r
9040 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9041 {\r
9042   ChildProc *cp; int result;\r
9043 \r
9044   cp = (ChildProc *) pr;\r
9045   if (cp == NULL) return;\r
9046 \r
9047   switch (cp->kind) {\r
9048   case CPReal:\r
9049     /* TerminateProcess is considered harmful, so... */\r
9050     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9051     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9052     /* The following doesn't work because the chess program\r
9053        doesn't "have the same console" as WinBoard.  Maybe\r
9054        we could arrange for this even though neither WinBoard\r
9055        nor the chess program uses a console for stdio? */\r
9056     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9057 \r
9058     /* [AS] Special termination modes for misbehaving programs... */\r
9059     if( signal == 9 ) { \r
9060         result = TerminateProcess( cp->hProcess, 0 );\r
9061 \r
9062         if ( appData.debugMode) {\r
9063             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9064         }\r
9065     }\r
9066     else if( signal == 10 ) {\r
9067         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9068 \r
9069         if( dw != WAIT_OBJECT_0 ) {\r
9070             result = TerminateProcess( cp->hProcess, 0 );\r
9071 \r
9072             if ( appData.debugMode) {\r
9073                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9074             }\r
9075 \r
9076         }\r
9077     }\r
9078 \r
9079     CloseHandle(cp->hProcess);\r
9080     break;\r
9081 \r
9082   case CPComm:\r
9083     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9084     break;\r
9085 \r
9086   case CPSock:\r
9087     closesocket(cp->sock);\r
9088     WSACleanup();\r
9089     break;\r
9090 \r
9091   case CPRcmd:\r
9092     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9093     closesocket(cp->sock);\r
9094     closesocket(cp->sock2);\r
9095     WSACleanup();\r
9096     break;\r
9097   }\r
9098   free(cp);\r
9099 }\r
9100 \r
9101 void\r
9102 InterruptChildProcess(ProcRef pr)\r
9103 {\r
9104   ChildProc *cp;\r
9105 \r
9106   cp = (ChildProc *) pr;\r
9107   if (cp == NULL) return;\r
9108   switch (cp->kind) {\r
9109   case CPReal:\r
9110     /* The following doesn't work because the chess program\r
9111        doesn't "have the same console" as WinBoard.  Maybe\r
9112        we could arrange for this even though neither WinBoard\r
9113        nor the chess program uses a console for stdio */\r
9114     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9115     break;\r
9116 \r
9117   case CPComm:\r
9118   case CPSock:\r
9119     /* Can't interrupt */\r
9120     break;\r
9121 \r
9122   case CPRcmd:\r
9123     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9124     break;\r
9125   }\r
9126 }\r
9127 \r
9128 \r
9129 int\r
9130 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9131 {\r
9132   char cmdLine[MSG_SIZ];\r
9133 \r
9134   if (port[0] == NULLCHAR) {\r
9135     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9136   } else {\r
9137     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9138   }\r
9139   return StartChildProcess(cmdLine, "", pr);\r
9140 }\r
9141 \r
9142 \r
9143 /* Code to open TCP sockets */\r
9144 \r
9145 int\r
9146 OpenTCP(char *host, char *port, ProcRef *pr)\r
9147 {\r
9148   ChildProc *cp;\r
9149   int err;\r
9150   SOCKET s;\r
9151   struct sockaddr_in sa, mysa;\r
9152   struct hostent FAR *hp;\r
9153   unsigned short uport;\r
9154   WORD wVersionRequested;\r
9155   WSADATA wsaData;\r
9156 \r
9157   /* Initialize socket DLL */\r
9158   wVersionRequested = MAKEWORD(1, 1);\r
9159   err = WSAStartup(wVersionRequested, &wsaData);\r
9160   if (err != 0) return err;\r
9161 \r
9162   /* Make socket */\r
9163   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9164     err = WSAGetLastError();\r
9165     WSACleanup();\r
9166     return err;\r
9167   }\r
9168 \r
9169   /* Bind local address using (mostly) don't-care values.\r
9170    */\r
9171   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9172   mysa.sin_family = AF_INET;\r
9173   mysa.sin_addr.s_addr = INADDR_ANY;\r
9174   uport = (unsigned short) 0;\r
9175   mysa.sin_port = htons(uport);\r
9176   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9177       == SOCKET_ERROR) {\r
9178     err = WSAGetLastError();\r
9179     WSACleanup();\r
9180     return err;\r
9181   }\r
9182 \r
9183   /* Resolve remote host name */\r
9184   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9185   if (!(hp = gethostbyname(host))) {\r
9186     unsigned int b0, b1, b2, b3;\r
9187 \r
9188     err = WSAGetLastError();\r
9189 \r
9190     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9191       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9192       hp->h_addrtype = AF_INET;\r
9193       hp->h_length = 4;\r
9194       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9195       hp->h_addr_list[0] = (char *) malloc(4);\r
9196       hp->h_addr_list[0][0] = (char) b0;\r
9197       hp->h_addr_list[0][1] = (char) b1;\r
9198       hp->h_addr_list[0][2] = (char) b2;\r
9199       hp->h_addr_list[0][3] = (char) b3;\r
9200     } else {\r
9201       WSACleanup();\r
9202       return err;\r
9203     }\r
9204   }\r
9205   sa.sin_family = hp->h_addrtype;\r
9206   uport = (unsigned short) atoi(port);\r
9207   sa.sin_port = htons(uport);\r
9208   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9209 \r
9210   /* Make connection */\r
9211   if (connect(s, (struct sockaddr *) &sa,\r
9212               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9213     err = WSAGetLastError();\r
9214     WSACleanup();\r
9215     return err;\r
9216   }\r
9217 \r
9218   /* Prepare return value */\r
9219   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9220   cp->kind = CPSock;\r
9221   cp->sock = s;\r
9222   *pr = (ProcRef *) cp;\r
9223 \r
9224   return NO_ERROR;\r
9225 }\r
9226 \r
9227 int\r
9228 OpenCommPort(char *name, ProcRef *pr)\r
9229 {\r
9230   HANDLE h;\r
9231   COMMTIMEOUTS ct;\r
9232   ChildProc *cp;\r
9233   char fullname[MSG_SIZ];\r
9234 \r
9235   if (*name != '\\')\r
9236     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9237   else\r
9238     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9239 \r
9240   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9241                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9242   if (h == (HANDLE) -1) {\r
9243     return GetLastError();\r
9244   }\r
9245   hCommPort = h;\r
9246 \r
9247   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9248 \r
9249   /* Accumulate characters until a 100ms pause, then parse */\r
9250   ct.ReadIntervalTimeout = 100;\r
9251   ct.ReadTotalTimeoutMultiplier = 0;\r
9252   ct.ReadTotalTimeoutConstant = 0;\r
9253   ct.WriteTotalTimeoutMultiplier = 0;\r
9254   ct.WriteTotalTimeoutConstant = 0;\r
9255   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9256 \r
9257   /* Prepare return value */\r
9258   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9259   cp->kind = CPComm;\r
9260   cp->hFrom = h;\r
9261   cp->hTo = h;\r
9262   *pr = (ProcRef *) cp;\r
9263 \r
9264   return NO_ERROR;\r
9265 }\r
9266 \r
9267 int\r
9268 OpenLoopback(ProcRef *pr)\r
9269 {\r
9270   DisplayFatalError(_("Not implemented"), 0, 1);\r
9271   return NO_ERROR;\r
9272 }\r
9273 \r
9274 \r
9275 int\r
9276 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9277 {\r
9278   ChildProc *cp;\r
9279   int err;\r
9280   SOCKET s, s2, s3;\r
9281   struct sockaddr_in sa, mysa;\r
9282   struct hostent FAR *hp;\r
9283   unsigned short uport;\r
9284   WORD wVersionRequested;\r
9285   WSADATA wsaData;\r
9286   int fromPort;\r
9287   char stderrPortStr[MSG_SIZ];\r
9288 \r
9289   /* Initialize socket DLL */\r
9290   wVersionRequested = MAKEWORD(1, 1);\r
9291   err = WSAStartup(wVersionRequested, &wsaData);\r
9292   if (err != 0) return err;\r
9293 \r
9294   /* Resolve remote host name */\r
9295   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9296   if (!(hp = gethostbyname(host))) {\r
9297     unsigned int b0, b1, b2, b3;\r
9298 \r
9299     err = WSAGetLastError();\r
9300 \r
9301     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9302       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9303       hp->h_addrtype = AF_INET;\r
9304       hp->h_length = 4;\r
9305       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9306       hp->h_addr_list[0] = (char *) malloc(4);\r
9307       hp->h_addr_list[0][0] = (char) b0;\r
9308       hp->h_addr_list[0][1] = (char) b1;\r
9309       hp->h_addr_list[0][2] = (char) b2;\r
9310       hp->h_addr_list[0][3] = (char) b3;\r
9311     } else {\r
9312       WSACleanup();\r
9313       return err;\r
9314     }\r
9315   }\r
9316   sa.sin_family = hp->h_addrtype;\r
9317   uport = (unsigned short) 514;\r
9318   sa.sin_port = htons(uport);\r
9319   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9320 \r
9321   /* Bind local socket to unused "privileged" port address\r
9322    */\r
9323   s = INVALID_SOCKET;\r
9324   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9325   mysa.sin_family = AF_INET;\r
9326   mysa.sin_addr.s_addr = INADDR_ANY;\r
9327   for (fromPort = 1023;; fromPort--) {\r
9328     if (fromPort < 0) {\r
9329       WSACleanup();\r
9330       return WSAEADDRINUSE;\r
9331     }\r
9332     if (s == INVALID_SOCKET) {\r
9333       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9334         err = WSAGetLastError();\r
9335         WSACleanup();\r
9336         return err;\r
9337       }\r
9338     }\r
9339     uport = (unsigned short) fromPort;\r
9340     mysa.sin_port = htons(uport);\r
9341     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9342         == SOCKET_ERROR) {\r
9343       err = WSAGetLastError();\r
9344       if (err == WSAEADDRINUSE) continue;\r
9345       WSACleanup();\r
9346       return err;\r
9347     }\r
9348     if (connect(s, (struct sockaddr *) &sa,\r
9349       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9350       err = WSAGetLastError();\r
9351       if (err == WSAEADDRINUSE) {\r
9352         closesocket(s);\r
9353         s = -1;\r
9354         continue;\r
9355       }\r
9356       WSACleanup();\r
9357       return err;\r
9358     }\r
9359     break;\r
9360   }\r
9361 \r
9362   /* Bind stderr local socket to unused "privileged" port address\r
9363    */\r
9364   s2 = INVALID_SOCKET;\r
9365   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9366   mysa.sin_family = AF_INET;\r
9367   mysa.sin_addr.s_addr = INADDR_ANY;\r
9368   for (fromPort = 1023;; fromPort--) {\r
9369     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9370     if (fromPort < 0) {\r
9371       (void) closesocket(s);\r
9372       WSACleanup();\r
9373       return WSAEADDRINUSE;\r
9374     }\r
9375     if (s2 == INVALID_SOCKET) {\r
9376       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9377         err = WSAGetLastError();\r
9378         closesocket(s);\r
9379         WSACleanup();\r
9380         return err;\r
9381       }\r
9382     }\r
9383     uport = (unsigned short) fromPort;\r
9384     mysa.sin_port = htons(uport);\r
9385     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9386         == SOCKET_ERROR) {\r
9387       err = WSAGetLastError();\r
9388       if (err == WSAEADDRINUSE) continue;\r
9389       (void) closesocket(s);\r
9390       WSACleanup();\r
9391       return err;\r
9392     }\r
9393     if (listen(s2, 1) == SOCKET_ERROR) {\r
9394       err = WSAGetLastError();\r
9395       if (err == WSAEADDRINUSE) {\r
9396         closesocket(s2);\r
9397         s2 = INVALID_SOCKET;\r
9398         continue;\r
9399       }\r
9400       (void) closesocket(s);\r
9401       (void) closesocket(s2);\r
9402       WSACleanup();\r
9403       return err;\r
9404     }\r
9405     break;\r
9406   }\r
9407   prevStderrPort = fromPort; // remember port used\r
9408   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9409 \r
9410   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9411     err = WSAGetLastError();\r
9412     (void) closesocket(s);\r
9413     (void) closesocket(s2);\r
9414     WSACleanup();\r
9415     return err;\r
9416   }\r
9417 \r
9418   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9419     err = WSAGetLastError();\r
9420     (void) closesocket(s);\r
9421     (void) closesocket(s2);\r
9422     WSACleanup();\r
9423     return err;\r
9424   }\r
9425   if (*user == NULLCHAR) user = UserName();\r
9426   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9427     err = WSAGetLastError();\r
9428     (void) closesocket(s);\r
9429     (void) closesocket(s2);\r
9430     WSACleanup();\r
9431     return err;\r
9432   }\r
9433   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9434     err = WSAGetLastError();\r
9435     (void) closesocket(s);\r
9436     (void) closesocket(s2);\r
9437     WSACleanup();\r
9438     return err;\r
9439   }\r
9440 \r
9441   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9442     err = WSAGetLastError();\r
9443     (void) closesocket(s);\r
9444     (void) closesocket(s2);\r
9445     WSACleanup();\r
9446     return err;\r
9447   }\r
9448   (void) closesocket(s2);  /* Stop listening */\r
9449 \r
9450   /* Prepare return value */\r
9451   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9452   cp->kind = CPRcmd;\r
9453   cp->sock = s;\r
9454   cp->sock2 = s3;\r
9455   *pr = (ProcRef *) cp;\r
9456 \r
9457   return NO_ERROR;\r
9458 }\r
9459 \r
9460 \r
9461 InputSourceRef\r
9462 AddInputSource(ProcRef pr, int lineByLine,\r
9463                InputCallback func, VOIDSTAR closure)\r
9464 {\r
9465   InputSource *is, *is2 = NULL;\r
9466   ChildProc *cp = (ChildProc *) pr;\r
9467 \r
9468   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9469   is->lineByLine = lineByLine;\r
9470   is->func = func;\r
9471   is->closure = closure;\r
9472   is->second = NULL;\r
9473   is->next = is->buf;\r
9474   if (pr == NoProc) {\r
9475     is->kind = CPReal;\r
9476     consoleInputSource = is;\r
9477   } else {\r
9478     is->kind = cp->kind;\r
9479     /* \r
9480         [AS] Try to avoid a race condition if the thread is given control too early:\r
9481         we create all threads suspended so that the is->hThread variable can be\r
9482         safely assigned, then let the threads start with ResumeThread.\r
9483     */\r
9484     switch (cp->kind) {\r
9485     case CPReal:\r
9486       is->hFile = cp->hFrom;\r
9487       cp->hFrom = NULL; /* now owned by InputThread */\r
9488       is->hThread =\r
9489         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9490                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9491       break;\r
9492 \r
9493     case CPComm:\r
9494       is->hFile = cp->hFrom;\r
9495       cp->hFrom = NULL; /* now owned by InputThread */\r
9496       is->hThread =\r
9497         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9498                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9499       break;\r
9500 \r
9501     case CPSock:\r
9502       is->sock = cp->sock;\r
9503       is->hThread =\r
9504         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9505                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9506       break;\r
9507 \r
9508     case CPRcmd:\r
9509       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9510       *is2 = *is;\r
9511       is->sock = cp->sock;\r
9512       is->second = is2;\r
9513       is2->sock = cp->sock2;\r
9514       is2->second = is2;\r
9515       is->hThread =\r
9516         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9517                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9518       is2->hThread =\r
9519         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9520                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9521       break;\r
9522     }\r
9523 \r
9524     if( is->hThread != NULL ) {\r
9525         ResumeThread( is->hThread );\r
9526     }\r
9527 \r
9528     if( is2 != NULL && is2->hThread != NULL ) {\r
9529         ResumeThread( is2->hThread );\r
9530     }\r
9531   }\r
9532 \r
9533   return (InputSourceRef) is;\r
9534 }\r
9535 \r
9536 void\r
9537 RemoveInputSource(InputSourceRef isr)\r
9538 {\r
9539   InputSource *is;\r
9540 \r
9541   is = (InputSource *) isr;\r
9542   is->hThread = NULL;  /* tell thread to stop */\r
9543   CloseHandle(is->hThread);\r
9544   if (is->second != NULL) {\r
9545     is->second->hThread = NULL;\r
9546     CloseHandle(is->second->hThread);\r
9547   }\r
9548 }\r
9549 \r
9550 int no_wrap(char *message, int count)\r
9551 {\r
9552     ConsoleOutput(message, count, FALSE);\r
9553     return count;\r
9554 }\r
9555 \r
9556 int\r
9557 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9558 {\r
9559   DWORD dOutCount;\r
9560   int outCount = SOCKET_ERROR;\r
9561   ChildProc *cp = (ChildProc *) pr;\r
9562   static OVERLAPPED ovl;\r
9563   static int line = 0;\r
9564 \r
9565   if (pr == NoProc)\r
9566   {\r
9567     if (appData.noJoin || !appData.useInternalWrap)\r
9568       return no_wrap(message, count);\r
9569     else\r
9570     {\r
9571       int width = get_term_width();\r
9572       int len = wrap(NULL, message, count, width, &line);\r
9573       char *msg = malloc(len);\r
9574       int dbgchk;\r
9575 \r
9576       if (!msg)\r
9577         return no_wrap(message, count);\r
9578       else\r
9579       {\r
9580         dbgchk = wrap(msg, message, count, width, &line);\r
9581         if (dbgchk != len && appData.debugMode)\r
9582             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9583         ConsoleOutput(msg, len, FALSE);\r
9584         free(msg);\r
9585         return len;\r
9586       }\r
9587     }\r
9588   }\r
9589 \r
9590   if (ovl.hEvent == NULL) {\r
9591     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9592   }\r
9593   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9594 \r
9595   switch (cp->kind) {\r
9596   case CPSock:\r
9597   case CPRcmd:\r
9598     outCount = send(cp->sock, message, count, 0);\r
9599     if (outCount == SOCKET_ERROR) {\r
9600       *outError = WSAGetLastError();\r
9601     } else {\r
9602       *outError = NO_ERROR;\r
9603     }\r
9604     break;\r
9605 \r
9606   case CPReal:\r
9607     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9608                   &dOutCount, NULL)) {\r
9609       *outError = NO_ERROR;\r
9610       outCount = (int) dOutCount;\r
9611     } else {\r
9612       *outError = GetLastError();\r
9613     }\r
9614     break;\r
9615 \r
9616   case CPComm:\r
9617     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9618                             &dOutCount, &ovl);\r
9619     if (*outError == NO_ERROR) {\r
9620       outCount = (int) dOutCount;\r
9621     }\r
9622     break;\r
9623   }\r
9624   return outCount;\r
9625 }\r
9626 \r
9627 void\r
9628 DoSleep(int n)\r
9629 {\r
9630     if(n != 0) Sleep(n);\r
9631 }\r
9632 \r
9633 int\r
9634 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9635                        long msdelay)\r
9636 {\r
9637   /* Ignore delay, not implemented for WinBoard */\r
9638   return OutputToProcess(pr, message, count, outError);\r
9639 }\r
9640 \r
9641 \r
9642 void\r
9643 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9644                         char *buf, int count, int error)\r
9645 {\r
9646   DisplayFatalError(_("Not implemented"), 0, 1);\r
9647 }\r
9648 \r
9649 /* see wgamelist.c for Game List functions */\r
9650 /* see wedittags.c for Edit Tags functions */\r
9651 \r
9652 \r
9653 VOID\r
9654 ICSInitScript()\r
9655 {\r
9656   FILE *f;\r
9657   char buf[MSG_SIZ];\r
9658   char *dummy;\r
9659 \r
9660   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9661     f = fopen(buf, "r");\r
9662     if (f != NULL) {\r
9663       ProcessICSInitScript(f);\r
9664       fclose(f);\r
9665     }\r
9666   }\r
9667 }\r
9668 \r
9669 \r
9670 VOID\r
9671 StartAnalysisClock()\r
9672 {\r
9673   if (analysisTimerEvent) return;\r
9674   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9675                                         (UINT) 2000, NULL);\r
9676 }\r
9677 \r
9678 VOID\r
9679 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9680 {\r
9681   highlightInfo.sq[0].x = fromX;\r
9682   highlightInfo.sq[0].y = fromY;\r
9683   highlightInfo.sq[1].x = toX;\r
9684   highlightInfo.sq[1].y = toY;\r
9685 }\r
9686 \r
9687 VOID\r
9688 ClearHighlights()\r
9689 {\r
9690   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9691     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9692 }\r
9693 \r
9694 VOID\r
9695 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9696 {\r
9697   premoveHighlightInfo.sq[0].x = fromX;\r
9698   premoveHighlightInfo.sq[0].y = fromY;\r
9699   premoveHighlightInfo.sq[1].x = toX;\r
9700   premoveHighlightInfo.sq[1].y = toY;\r
9701 }\r
9702 \r
9703 VOID\r
9704 ClearPremoveHighlights()\r
9705 {\r
9706   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9707     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9708 }\r
9709 \r
9710 VOID\r
9711 ShutDownFrontEnd()\r
9712 {\r
9713   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9714   DeleteClipboardTempFiles();\r
9715 }\r
9716 \r
9717 void\r
9718 BoardToTop()\r
9719 {\r
9720     if (IsIconic(hwndMain))\r
9721       ShowWindow(hwndMain, SW_RESTORE);\r
9722 \r
9723     SetActiveWindow(hwndMain);\r
9724 }\r
9725 \r
9726 /*\r
9727  * Prototypes for animation support routines\r
9728  */\r
9729 static void ScreenSquare(int column, int row, POINT * pt);\r
9730 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9731      POINT frames[], int * nFrames);\r
9732 \r
9733 \r
9734 #define kFactor 4\r
9735 \r
9736 void\r
9737 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9738 {       // [HGM] atomic: animate blast wave\r
9739         int i;\r
9740 \r
9741         explodeInfo.fromX = fromX;\r
9742         explodeInfo.fromY = fromY;\r
9743         explodeInfo.toX = toX;\r
9744         explodeInfo.toY = toY;\r
9745         for(i=1; i<4*kFactor; i++) {\r
9746             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9747             DrawPosition(FALSE, board);\r
9748             Sleep(appData.animSpeed);\r
9749         }\r
9750         explodeInfo.radius = 0;\r
9751         DrawPosition(TRUE, board);\r
9752 }\r
9753 \r
9754 void\r
9755 AnimateMove(board, fromX, fromY, toX, toY)\r
9756      Board board;\r
9757      int fromX;\r
9758      int fromY;\r
9759      int toX;\r
9760      int toY;\r
9761 {\r
9762   ChessSquare piece;\r
9763   POINT start, finish, mid;\r
9764   POINT frames[kFactor * 2 + 1];\r
9765   int nFrames, n;\r
9766 \r
9767   if (!appData.animate) return;\r
9768   if (doingSizing) return;\r
9769   if (fromY < 0 || fromX < 0) return;\r
9770   piece = board[fromY][fromX];\r
9771   if (piece >= EmptySquare) return;\r
9772 \r
9773   ScreenSquare(fromX, fromY, &start);\r
9774   ScreenSquare(toX, toY, &finish);\r
9775 \r
9776   /* All moves except knight jumps move in straight line */\r
9777   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9778     mid.x = start.x + (finish.x - start.x) / 2;\r
9779     mid.y = start.y + (finish.y - start.y) / 2;\r
9780   } else {\r
9781     /* Knight: make straight movement then diagonal */\r
9782     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9783        mid.x = start.x + (finish.x - start.x) / 2;\r
9784        mid.y = start.y;\r
9785      } else {\r
9786        mid.x = start.x;\r
9787        mid.y = start.y + (finish.y - start.y) / 2;\r
9788      }\r
9789   }\r
9790   \r
9791   /* Don't use as many frames for very short moves */\r
9792   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9793     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9794   else\r
9795     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9796 \r
9797   animInfo.from.x = fromX;\r
9798   animInfo.from.y = fromY;\r
9799   animInfo.to.x = toX;\r
9800   animInfo.to.y = toY;\r
9801   animInfo.lastpos = start;\r
9802   animInfo.piece = piece;\r
9803   for (n = 0; n < nFrames; n++) {\r
9804     animInfo.pos = frames[n];\r
9805     DrawPosition(FALSE, NULL);\r
9806     animInfo.lastpos = animInfo.pos;\r
9807     Sleep(appData.animSpeed);\r
9808   }\r
9809   animInfo.pos = finish;\r
9810   DrawPosition(FALSE, NULL);\r
9811   animInfo.piece = EmptySquare;\r
9812   Explode(board, fromX, fromY, toX, toY);\r
9813 }\r
9814 \r
9815 /*      Convert board position to corner of screen rect and color       */\r
9816 \r
9817 static void\r
9818 ScreenSquare(column, row, pt)\r
9819      int column; int row; POINT * pt;\r
9820 {\r
9821   if (flipView) {\r
9822     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9823     pt->y = lineGap + row * (squareSize + lineGap);\r
9824   } else {\r
9825     pt->x = lineGap + column * (squareSize + lineGap);\r
9826     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9827   }\r
9828 }\r
9829 \r
9830 /*      Generate a series of frame coords from start->mid->finish.\r
9831         The movement rate doubles until the half way point is\r
9832         reached, then halves back down to the final destination,\r
9833         which gives a nice slow in/out effect. The algorithmn\r
9834         may seem to generate too many intermediates for short\r
9835         moves, but remember that the purpose is to attract the\r
9836         viewers attention to the piece about to be moved and\r
9837         then to where it ends up. Too few frames would be less\r
9838         noticeable.                                             */\r
9839 \r
9840 static void\r
9841 Tween(start, mid, finish, factor, frames, nFrames)\r
9842      POINT * start; POINT * mid;\r
9843      POINT * finish; int factor;\r
9844      POINT frames[]; int * nFrames;\r
9845 {\r
9846   int n, fraction = 1, count = 0;\r
9847 \r
9848   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9849   for (n = 0; n < factor; n++)\r
9850     fraction *= 2;\r
9851   for (n = 0; n < factor; n++) {\r
9852     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9853     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9854     count ++;\r
9855     fraction = fraction / 2;\r
9856   }\r
9857   \r
9858   /* Midpoint */\r
9859   frames[count] = *mid;\r
9860   count ++;\r
9861   \r
9862   /* Slow out, stepping 1/2, then 1/4, ... */\r
9863   fraction = 2;\r
9864   for (n = 0; n < factor; n++) {\r
9865     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9866     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9867     count ++;\r
9868     fraction = fraction * 2;\r
9869   }\r
9870   *nFrames = count;\r
9871 }\r
9872 \r
9873 void\r
9874 SettingsPopUp(ChessProgramState *cps)\r
9875 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9876       EngineOptionsPopup(savedHwnd, cps);\r
9877 }\r
9878 \r
9879 int flock(int fid, int code)\r
9880 {\r
9881     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9882     OVERLAPPED ov;\r
9883     ov.hEvent = NULL;\r
9884     ov.Offset = 0;\r
9885     ov.OffsetHigh = 0;\r
9886     switch(code) {\r
9887       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9888       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9889       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9890       default: return -1;\r
9891     }\r
9892     return 0;\r
9893 }\r