Add -afterGame option
[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, \r
257   OPT_elo1, OPT_elo2, OPT_date, 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 IDM_Revert:\r
5067       RevertEvent(FALSE);\r
5068       break;\r
5069 \r
5070     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5071       RevertEvent(TRUE);\r
5072       break;\r
5073 \r
5074     case IDM_TruncateGame:\r
5075       TruncateGameEvent();\r
5076       break;\r
5077 \r
5078     case IDM_MoveNow:\r
5079       MoveNowEvent();\r
5080       break;\r
5081 \r
5082     case IDM_RetractMove:\r
5083       RetractMoveEvent();\r
5084       break;\r
5085 \r
5086     case IDM_FlipView:\r
5087       flipView = !flipView;\r
5088       DrawPosition(FALSE, NULL);\r
5089       break;\r
5090 \r
5091     case IDM_FlipClock:\r
5092       flipClock = !flipClock;\r
5093       DisplayBothClocks();\r
5094       DisplayLogos();\r
5095       break;\r
5096 \r
5097     case IDM_MuteSounds:\r
5098       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5099       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5100                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5101       break;\r
5102 \r
5103     case IDM_GeneralOptions:\r
5104       GeneralOptionsPopup(hwnd);\r
5105       DrawPosition(TRUE, NULL);\r
5106       break;\r
5107 \r
5108     case IDM_BoardOptions:\r
5109       BoardOptionsPopup(hwnd);\r
5110       break;\r
5111 \r
5112     case IDM_EnginePlayOptions:\r
5113       EnginePlayOptionsPopup(hwnd);\r
5114       break;\r
5115 \r
5116     case IDM_Engine1Options:\r
5117       EngineOptionsPopup(hwnd, &first);\r
5118       break;\r
5119 \r
5120     case IDM_Engine2Options:\r
5121       savedHwnd = hwnd;\r
5122       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5123       EngineOptionsPopup(hwnd, &second);\r
5124       break;\r
5125 \r
5126     case IDM_OptionsUCI:\r
5127       UciOptionsPopup(hwnd);\r
5128       break;\r
5129 \r
5130     case IDM_Tourney:\r
5131       TourneyPopup(hwnd);\r
5132       break;\r
5133 \r
5134     case IDM_IcsOptions:\r
5135       IcsOptionsPopup(hwnd);\r
5136       break;\r
5137 \r
5138     case IDM_Fonts:\r
5139       FontsOptionsPopup(hwnd);\r
5140       break;\r
5141 \r
5142     case IDM_Sounds:\r
5143       SoundOptionsPopup(hwnd);\r
5144       break;\r
5145 \r
5146     case IDM_CommPort:\r
5147       CommPortOptionsPopup(hwnd);\r
5148       break;\r
5149 \r
5150     case IDM_LoadOptions:\r
5151       LoadOptionsPopup(hwnd);\r
5152       break;\r
5153 \r
5154     case IDM_SaveOptions:\r
5155       SaveOptionsPopup(hwnd);\r
5156       break;\r
5157 \r
5158     case IDM_TimeControl:\r
5159       TimeControlOptionsPopup(hwnd);\r
5160       break;\r
5161 \r
5162     case IDM_SaveSettings:\r
5163       SaveSettings(settingsFileName);\r
5164       break;\r
5165 \r
5166     case IDM_SaveSettingsOnExit:\r
5167       saveSettingsOnExit = !saveSettingsOnExit;\r
5168       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5169                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5170                                          MF_CHECKED : MF_UNCHECKED));\r
5171       break;\r
5172 \r
5173     case IDM_Hint:\r
5174       HintEvent();\r
5175       break;\r
5176 \r
5177     case IDM_Book:\r
5178       BookEvent();\r
5179       break;\r
5180 \r
5181     case IDM_AboutGame:\r
5182       AboutGameEvent();\r
5183       break;\r
5184 \r
5185     case IDM_Debug:\r
5186       appData.debugMode = !appData.debugMode;\r
5187       if (appData.debugMode) {\r
5188         char dir[MSG_SIZ];\r
5189         GetCurrentDirectory(MSG_SIZ, dir);\r
5190         SetCurrentDirectory(installDir);\r
5191         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5192         SetCurrentDirectory(dir);\r
5193         setbuf(debugFP, NULL);\r
5194       } else {\r
5195         fclose(debugFP);\r
5196         debugFP = NULL;\r
5197       }\r
5198       break;\r
5199 \r
5200     case IDM_HELPCONTENTS:\r
5201       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5202           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5203           MessageBox (GetFocus(),\r
5204                     _("Unable to activate help"),\r
5205                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5206       }\r
5207       break;\r
5208 \r
5209     case IDM_HELPSEARCH:\r
5210         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5211             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5212         MessageBox (GetFocus(),\r
5213                     _("Unable to activate help"),\r
5214                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5215       }\r
5216       break;\r
5217 \r
5218     case IDM_HELPHELP:\r
5219       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5220         MessageBox (GetFocus(),\r
5221                     _("Unable to activate help"),\r
5222                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5223       }\r
5224       break;\r
5225 \r
5226     case IDM_ABOUT:\r
5227       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5228       DialogBox(hInst, \r
5229         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5230         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5231       FreeProcInstance(lpProc);\r
5232       break;\r
5233 \r
5234     case IDM_DirectCommand1:\r
5235       AskQuestionEvent(_("Direct Command"),\r
5236                        _("Send to chess program:"), "", "1");\r
5237       break;\r
5238     case IDM_DirectCommand2:\r
5239       AskQuestionEvent(_("Direct Command"),\r
5240                        _("Send to second chess program:"), "", "2");\r
5241       break;\r
5242 \r
5243     case EP_WhitePawn:\r
5244       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5245       fromX = fromY = -1;\r
5246       break;\r
5247 \r
5248     case EP_WhiteKnight:\r
5249       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5250       fromX = fromY = -1;\r
5251       break;\r
5252 \r
5253     case EP_WhiteBishop:\r
5254       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5255       fromX = fromY = -1;\r
5256       break;\r
5257 \r
5258     case EP_WhiteRook:\r
5259       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5260       fromX = fromY = -1;\r
5261       break;\r
5262 \r
5263     case EP_WhiteQueen:\r
5264       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5265       fromX = fromY = -1;\r
5266       break;\r
5267 \r
5268     case EP_WhiteFerz:\r
5269       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5270       fromX = fromY = -1;\r
5271       break;\r
5272 \r
5273     case EP_WhiteWazir:\r
5274       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5275       fromX = fromY = -1;\r
5276       break;\r
5277 \r
5278     case EP_WhiteAlfil:\r
5279       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5280       fromX = fromY = -1;\r
5281       break;\r
5282 \r
5283     case EP_WhiteCannon:\r
5284       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5285       fromX = fromY = -1;\r
5286       break;\r
5287 \r
5288     case EP_WhiteCardinal:\r
5289       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5290       fromX = fromY = -1;\r
5291       break;\r
5292 \r
5293     case EP_WhiteMarshall:\r
5294       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5295       fromX = fromY = -1;\r
5296       break;\r
5297 \r
5298     case EP_WhiteKing:\r
5299       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5300       fromX = fromY = -1;\r
5301       break;\r
5302 \r
5303     case EP_BlackPawn:\r
5304       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5305       fromX = fromY = -1;\r
5306       break;\r
5307 \r
5308     case EP_BlackKnight:\r
5309       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5310       fromX = fromY = -1;\r
5311       break;\r
5312 \r
5313     case EP_BlackBishop:\r
5314       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5315       fromX = fromY = -1;\r
5316       break;\r
5317 \r
5318     case EP_BlackRook:\r
5319       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5320       fromX = fromY = -1;\r
5321       break;\r
5322 \r
5323     case EP_BlackQueen:\r
5324       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5325       fromX = fromY = -1;\r
5326       break;\r
5327 \r
5328     case EP_BlackFerz:\r
5329       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5330       fromX = fromY = -1;\r
5331       break;\r
5332 \r
5333     case EP_BlackWazir:\r
5334       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5335       fromX = fromY = -1;\r
5336       break;\r
5337 \r
5338     case EP_BlackAlfil:\r
5339       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5340       fromX = fromY = -1;\r
5341       break;\r
5342 \r
5343     case EP_BlackCannon:\r
5344       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5345       fromX = fromY = -1;\r
5346       break;\r
5347 \r
5348     case EP_BlackCardinal:\r
5349       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5350       fromX = fromY = -1;\r
5351       break;\r
5352 \r
5353     case EP_BlackMarshall:\r
5354       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5355       fromX = fromY = -1;\r
5356       break;\r
5357 \r
5358     case EP_BlackKing:\r
5359       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5360       fromX = fromY = -1;\r
5361       break;\r
5362 \r
5363     case EP_EmptySquare:\r
5364       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5365       fromX = fromY = -1;\r
5366       break;\r
5367 \r
5368     case EP_ClearBoard:\r
5369       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5370       fromX = fromY = -1;\r
5371       break;\r
5372 \r
5373     case EP_White:\r
5374       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5375       fromX = fromY = -1;\r
5376       break;\r
5377 \r
5378     case EP_Black:\r
5379       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5380       fromX = fromY = -1;\r
5381       break;\r
5382 \r
5383     case EP_Promote:\r
5384       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5385       fromX = fromY = -1;\r
5386       break;\r
5387 \r
5388     case EP_Demote:\r
5389       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5390       fromX = fromY = -1;\r
5391       break;\r
5392 \r
5393     case DP_Pawn:\r
5394       DropMenuEvent(WhitePawn, fromX, fromY);\r
5395       fromX = fromY = -1;\r
5396       break;\r
5397 \r
5398     case DP_Knight:\r
5399       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5400       fromX = fromY = -1;\r
5401       break;\r
5402 \r
5403     case DP_Bishop:\r
5404       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5405       fromX = fromY = -1;\r
5406       break;\r
5407 \r
5408     case DP_Rook:\r
5409       DropMenuEvent(WhiteRook, fromX, fromY);\r
5410       fromX = fromY = -1;\r
5411       break;\r
5412 \r
5413     case DP_Queen:\r
5414       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5415       fromX = fromY = -1;\r
5416       break;\r
5417 \r
5418     case IDM_English:\r
5419       barbaric = 0; appData.language = "";\r
5420       TranslateMenus(0);\r
5421       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5422       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5423       lastChecked = wmId;\r
5424       break;\r
5425 \r
5426     default:\r
5427       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5428           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5429           TranslateMenus(0);\r
5430           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5431           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5432           lastChecked = wmId;\r
5433           break;\r
5434       }\r
5435       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5436     }\r
5437     break;\r
5438 \r
5439   case WM_TIMER:\r
5440     switch (wParam) {\r
5441     case CLOCK_TIMER_ID:\r
5442       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5443       clockTimerEvent = 0;\r
5444       DecrementClocks(); /* call into back end */\r
5445       break;\r
5446     case LOAD_GAME_TIMER_ID:\r
5447       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5448       loadGameTimerEvent = 0;\r
5449       AutoPlayGameLoop(); /* call into back end */\r
5450       break;\r
5451     case ANALYSIS_TIMER_ID:\r
5452       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5453                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5454         AnalysisPeriodicEvent(0);\r
5455       } else {\r
5456         KillTimer(hwnd, analysisTimerEvent);\r
5457         analysisTimerEvent = 0;\r
5458       }\r
5459       break;\r
5460     case DELAYED_TIMER_ID:\r
5461       KillTimer(hwnd, delayedTimerEvent);\r
5462       delayedTimerEvent = 0;\r
5463       delayedTimerCallback();\r
5464       break;\r
5465     }\r
5466     break;\r
5467 \r
5468   case WM_USER_Input:\r
5469     InputEvent(hwnd, message, wParam, lParam);\r
5470     break;\r
5471 \r
5472   /* [AS] Also move "attached" child windows */\r
5473   case WM_WINDOWPOSCHANGING:\r
5474 \r
5475     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5476         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5477 \r
5478         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5479             /* Window is moving */\r
5480             RECT rcMain;\r
5481 \r
5482 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5483             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5484             rcMain.right  = wpMain.x + wpMain.width;\r
5485             rcMain.top    = wpMain.y;\r
5486             rcMain.bottom = wpMain.y + wpMain.height;\r
5487             \r
5488             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5489             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5490             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5491             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5492             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5493             wpMain.x = lpwp->x;\r
5494             wpMain.y = lpwp->y;\r
5495         }\r
5496     }\r
5497     break;\r
5498 \r
5499   /* [AS] Snapping */\r
5500   case WM_ENTERSIZEMOVE:\r
5501     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5502     if (hwnd == hwndMain) {\r
5503       doingSizing = TRUE;\r
5504       lastSizing = 0;\r
5505     }\r
5506     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5507     break;\r
5508 \r
5509   case WM_SIZING:\r
5510     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5511     if (hwnd == hwndMain) {\r
5512       lastSizing = wParam;\r
5513     }\r
5514     break;\r
5515 \r
5516   case WM_MOVING:\r
5517     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5518       return OnMoving( &sd, hwnd, wParam, lParam );\r
5519 \r
5520   case WM_EXITSIZEMOVE:\r
5521     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5522     if (hwnd == hwndMain) {\r
5523       RECT client;\r
5524       doingSizing = FALSE;\r
5525       InvalidateRect(hwnd, &boardRect, FALSE);\r
5526       GetClientRect(hwnd, &client);\r
5527       ResizeBoard(client.right, client.bottom, lastSizing);\r
5528       lastSizing = 0;\r
5529       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5530     }\r
5531     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5532     break;\r
5533 \r
5534   case WM_DESTROY: /* message: window being destroyed */\r
5535     PostQuitMessage(0);\r
5536     break;\r
5537 \r
5538   case WM_CLOSE:\r
5539     if (hwnd == hwndMain) {\r
5540       ExitEvent(0);\r
5541     }\r
5542     break;\r
5543 \r
5544   default:      /* Passes it on if unprocessed */\r
5545     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5546   }\r
5547   return 0;\r
5548 }\r
5549 \r
5550 /*---------------------------------------------------------------------------*\\r
5551  *\r
5552  * Misc utility routines\r
5553  *\r
5554 \*---------------------------------------------------------------------------*/\r
5555 \r
5556 /*\r
5557  * Decent random number generator, at least not as bad as Windows\r
5558  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5559  */\r
5560 unsigned int randstate;\r
5561 \r
5562 int\r
5563 myrandom(void)\r
5564 {\r
5565   randstate = randstate * 1664525 + 1013904223;\r
5566   return (int) randstate & 0x7fffffff;\r
5567 }\r
5568 \r
5569 void\r
5570 mysrandom(unsigned int seed)\r
5571 {\r
5572   randstate = seed;\r
5573 }\r
5574 \r
5575 \r
5576 /* \r
5577  * returns TRUE if user selects a different color, FALSE otherwise \r
5578  */\r
5579 \r
5580 BOOL\r
5581 ChangeColor(HWND hwnd, COLORREF *which)\r
5582 {\r
5583   static BOOL firstTime = TRUE;\r
5584   static DWORD customColors[16];\r
5585   CHOOSECOLOR cc;\r
5586   COLORREF newcolor;\r
5587   int i;\r
5588   ColorClass ccl;\r
5589 \r
5590   if (firstTime) {\r
5591     /* Make initial colors in use available as custom colors */\r
5592     /* Should we put the compiled-in defaults here instead? */\r
5593     i = 0;\r
5594     customColors[i++] = lightSquareColor & 0xffffff;\r
5595     customColors[i++] = darkSquareColor & 0xffffff;\r
5596     customColors[i++] = whitePieceColor & 0xffffff;\r
5597     customColors[i++] = blackPieceColor & 0xffffff;\r
5598     customColors[i++] = highlightSquareColor & 0xffffff;\r
5599     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5600 \r
5601     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5602       customColors[i++] = textAttribs[ccl].color;\r
5603     }\r
5604     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5605     firstTime = FALSE;\r
5606   }\r
5607 \r
5608   cc.lStructSize = sizeof(cc);\r
5609   cc.hwndOwner = hwnd;\r
5610   cc.hInstance = NULL;\r
5611   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5612   cc.lpCustColors = (LPDWORD) customColors;\r
5613   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5614 \r
5615   if (!ChooseColor(&cc)) return FALSE;\r
5616 \r
5617   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5618   if (newcolor == *which) return FALSE;\r
5619   *which = newcolor;\r
5620   return TRUE;\r
5621 \r
5622   /*\r
5623   InitDrawingColors();\r
5624   InvalidateRect(hwnd, &boardRect, FALSE);\r
5625   */\r
5626 }\r
5627 \r
5628 BOOLEAN\r
5629 MyLoadSound(MySound *ms)\r
5630 {\r
5631   BOOL ok = FALSE;\r
5632   struct stat st;\r
5633   FILE *f;\r
5634 \r
5635   if (ms->data && ms->flag) free(ms->data);\r
5636   ms->data = NULL;\r
5637 \r
5638   switch (ms->name[0]) {\r
5639   case NULLCHAR:\r
5640     /* Silence */\r
5641     ok = TRUE;\r
5642     break;\r
5643   case '$':\r
5644     /* System sound from Control Panel.  Don't preload here. */\r
5645     ok = TRUE;\r
5646     break;\r
5647   case '!':\r
5648     if (ms->name[1] == NULLCHAR) {\r
5649       /* "!" alone = silence */\r
5650       ok = TRUE;\r
5651     } else {\r
5652       /* Builtin wave resource.  Error if not found. */\r
5653       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5654       if (h == NULL) break;\r
5655       ms->data = (void *)LoadResource(hInst, h);\r
5656       ms->flag = 0; // not maloced, so cannot be freed!\r
5657       if (h == NULL) break;\r
5658       ok = TRUE;\r
5659     }\r
5660     break;\r
5661   default:\r
5662     /* .wav file.  Error if not found. */\r
5663     f = fopen(ms->name, "rb");\r
5664     if (f == NULL) break;\r
5665     if (fstat(fileno(f), &st) < 0) break;\r
5666     ms->data = malloc(st.st_size);\r
5667     ms->flag = 1;\r
5668     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5669     fclose(f);\r
5670     ok = TRUE;\r
5671     break;\r
5672   }\r
5673   if (!ok) {\r
5674     char buf[MSG_SIZ];\r
5675       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5676     DisplayError(buf, GetLastError());\r
5677   }\r
5678   return ok;\r
5679 }\r
5680 \r
5681 BOOLEAN\r
5682 MyPlaySound(MySound *ms)\r
5683 {\r
5684   BOOLEAN ok = FALSE;\r
5685 \r
5686   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5687   switch (ms->name[0]) {\r
5688   case NULLCHAR:\r
5689         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5690     /* Silence */\r
5691     ok = TRUE;\r
5692     break;\r
5693   case '$':\r
5694     /* System sound from Control Panel (deprecated feature).\r
5695        "$" alone or an unset sound name gets default beep (still in use). */\r
5696     if (ms->name[1]) {\r
5697       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5698     }\r
5699     if (!ok) ok = MessageBeep(MB_OK);\r
5700     break; \r
5701   case '!':\r
5702     /* Builtin wave resource, or "!" alone for silence */\r
5703     if (ms->name[1]) {\r
5704       if (ms->data == NULL) return FALSE;\r
5705       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5706     } else {\r
5707       ok = TRUE;\r
5708     }\r
5709     break;\r
5710   default:\r
5711     /* .wav file.  Error if not found. */\r
5712     if (ms->data == NULL) return FALSE;\r
5713     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5714     break;\r
5715   }\r
5716   /* Don't print an error: this can happen innocently if the sound driver\r
5717      is busy; for instance, if another instance of WinBoard is playing\r
5718      a sound at about the same time. */\r
5719   return ok;\r
5720 }\r
5721 \r
5722 \r
5723 LRESULT CALLBACK\r
5724 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5725 {\r
5726   BOOL ok;\r
5727   OPENFILENAME *ofn;\r
5728   static UINT *number; /* gross that this is static */\r
5729 \r
5730   switch (message) {\r
5731   case WM_INITDIALOG: /* message: initialize dialog box */\r
5732     /* Center the dialog over the application window */\r
5733     ofn = (OPENFILENAME *) lParam;\r
5734     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5735       number = (UINT *) ofn->lCustData;\r
5736       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5737     } else {\r
5738       number = NULL;\r
5739     }\r
5740     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5741     Translate(hDlg, 1536);\r
5742     return FALSE;  /* Allow for further processing */\r
5743 \r
5744   case WM_COMMAND:\r
5745     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5746       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5747     }\r
5748     return FALSE;  /* Allow for further processing */\r
5749   }\r
5750   return FALSE;\r
5751 }\r
5752 \r
5753 UINT APIENTRY\r
5754 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5755 {\r
5756   static UINT *number;\r
5757   OPENFILENAME *ofname;\r
5758   OFNOTIFY *ofnot;\r
5759   switch (uiMsg) {\r
5760   case WM_INITDIALOG:\r
5761     Translate(hdlg, DLG_IndexNumber);\r
5762     ofname = (OPENFILENAME *)lParam;\r
5763     number = (UINT *)(ofname->lCustData);\r
5764     break;\r
5765   case WM_NOTIFY:\r
5766     ofnot = (OFNOTIFY *)lParam;\r
5767     if (ofnot->hdr.code == CDN_FILEOK) {\r
5768       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5769     }\r
5770     break;\r
5771   }\r
5772   return 0;\r
5773 }\r
5774 \r
5775 \r
5776 FILE *\r
5777 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5778                char *nameFilt, char *dlgTitle, UINT *number,\r
5779                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5780 {\r
5781   OPENFILENAME openFileName;\r
5782   char buf1[MSG_SIZ];\r
5783   FILE *f;\r
5784 \r
5785   if (fileName == NULL) fileName = buf1;\r
5786   if (defName == NULL) {\r
5787     safeStrCpy(fileName, "*.", 3 );\r
5788     strcat(fileName, defExt);\r
5789   } else {\r
5790     safeStrCpy(fileName, defName, MSG_SIZ );\r
5791   }\r
5792     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5793   if (number) *number = 0;\r
5794 \r
5795   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5796   openFileName.hwndOwner         = hwnd;\r
5797   openFileName.hInstance         = (HANDLE) hInst;\r
5798   openFileName.lpstrFilter       = nameFilt;\r
5799   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5800   openFileName.nMaxCustFilter    = 0L;\r
5801   openFileName.nFilterIndex      = 1L;\r
5802   openFileName.lpstrFile         = fileName;\r
5803   openFileName.nMaxFile          = MSG_SIZ;\r
5804   openFileName.lpstrFileTitle    = fileTitle;\r
5805   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5806   openFileName.lpstrInitialDir   = NULL;\r
5807   openFileName.lpstrTitle        = dlgTitle;\r
5808   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5809     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5810     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5811     | (oldDialog ? 0 : OFN_EXPLORER);\r
5812   openFileName.nFileOffset       = 0;\r
5813   openFileName.nFileExtension    = 0;\r
5814   openFileName.lpstrDefExt       = defExt;\r
5815   openFileName.lCustData         = (LONG) number;\r
5816   openFileName.lpfnHook          = oldDialog ?\r
5817     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5818   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5819 \r
5820   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5821                         GetOpenFileName(&openFileName)) {\r
5822     /* open the file */\r
5823     f = fopen(openFileName.lpstrFile, write);\r
5824     if (f == NULL) {\r
5825       MessageBox(hwnd, _("File open failed"), NULL,\r
5826                  MB_OK|MB_ICONEXCLAMATION);\r
5827       return NULL;\r
5828     }\r
5829   } else {\r
5830     int err = CommDlgExtendedError();\r
5831     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5832     return FALSE;\r
5833   }\r
5834   return f;\r
5835 }\r
5836 \r
5837 \r
5838 \r
5839 VOID APIENTRY\r
5840 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5841 {\r
5842   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5843 \r
5844   /*\r
5845    * Get the first pop-up menu in the menu template. This is the\r
5846    * menu that TrackPopupMenu displays.\r
5847    */\r
5848   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5849   TranslateOneMenu(10, hmenuTrackPopup);\r
5850 \r
5851   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5852 \r
5853   /*\r
5854    * TrackPopup uses screen coordinates, so convert the\r
5855    * coordinates of the mouse click to screen coordinates.\r
5856    */\r
5857   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5858 \r
5859   /* Draw and track the floating pop-up menu. */\r
5860   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5861                  pt.x, pt.y, 0, hwnd, NULL);\r
5862 \r
5863   /* Destroy the menu.*/\r
5864   DestroyMenu(hmenu);\r
5865 }\r
5866    \r
5867 typedef struct {\r
5868   HWND hDlg, hText;\r
5869   int sizeX, sizeY, newSizeX, newSizeY;\r
5870   HDWP hdwp;\r
5871 } ResizeEditPlusButtonsClosure;\r
5872 \r
5873 BOOL CALLBACK\r
5874 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5875 {\r
5876   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5877   RECT rect;\r
5878   POINT pt;\r
5879 \r
5880   if (hChild == cl->hText) return TRUE;\r
5881   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5882   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5883   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5884   ScreenToClient(cl->hDlg, &pt);\r
5885   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5886     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5887   return TRUE;\r
5888 }\r
5889 \r
5890 /* Resize a dialog that has a (rich) edit field filling most of\r
5891    the top, with a row of buttons below */\r
5892 VOID\r
5893 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5894 {\r
5895   RECT rectText;\r
5896   int newTextHeight, newTextWidth;\r
5897   ResizeEditPlusButtonsClosure cl;\r
5898   \r
5899   /*if (IsIconic(hDlg)) return;*/\r
5900   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5901   \r
5902   cl.hdwp = BeginDeferWindowPos(8);\r
5903 \r
5904   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5905   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5906   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5907   if (newTextHeight < 0) {\r
5908     newSizeY += -newTextHeight;\r
5909     newTextHeight = 0;\r
5910   }\r
5911   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5912     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5913 \r
5914   cl.hDlg = hDlg;\r
5915   cl.hText = hText;\r
5916   cl.sizeX = sizeX;\r
5917   cl.sizeY = sizeY;\r
5918   cl.newSizeX = newSizeX;\r
5919   cl.newSizeY = newSizeY;\r
5920   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5921 \r
5922   EndDeferWindowPos(cl.hdwp);\r
5923 }\r
5924 \r
5925 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5926 {\r
5927     RECT    rChild, rParent;\r
5928     int     wChild, hChild, wParent, hParent;\r
5929     int     wScreen, hScreen, xNew, yNew;\r
5930     HDC     hdc;\r
5931 \r
5932     /* Get the Height and Width of the child window */\r
5933     GetWindowRect (hwndChild, &rChild);\r
5934     wChild = rChild.right - rChild.left;\r
5935     hChild = rChild.bottom - rChild.top;\r
5936 \r
5937     /* Get the Height and Width of the parent window */\r
5938     GetWindowRect (hwndParent, &rParent);\r
5939     wParent = rParent.right - rParent.left;\r
5940     hParent = rParent.bottom - rParent.top;\r
5941 \r
5942     /* Get the display limits */\r
5943     hdc = GetDC (hwndChild);\r
5944     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5945     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5946     ReleaseDC(hwndChild, hdc);\r
5947 \r
5948     /* Calculate new X position, then adjust for screen */\r
5949     xNew = rParent.left + ((wParent - wChild) /2);\r
5950     if (xNew < 0) {\r
5951         xNew = 0;\r
5952     } else if ((xNew+wChild) > wScreen) {\r
5953         xNew = wScreen - wChild;\r
5954     }\r
5955 \r
5956     /* Calculate new Y position, then adjust for screen */\r
5957     if( mode == 0 ) {\r
5958         yNew = rParent.top  + ((hParent - hChild) /2);\r
5959     }\r
5960     else {\r
5961         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5962     }\r
5963 \r
5964     if (yNew < 0) {\r
5965         yNew = 0;\r
5966     } else if ((yNew+hChild) > hScreen) {\r
5967         yNew = hScreen - hChild;\r
5968     }\r
5969 \r
5970     /* Set it, and return */\r
5971     return SetWindowPos (hwndChild, NULL,\r
5972                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5973 }\r
5974 \r
5975 /* Center one window over another */\r
5976 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5977 {\r
5978     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5979 }\r
5980 \r
5981 /*---------------------------------------------------------------------------*\\r
5982  *\r
5983  * Startup Dialog functions\r
5984  *\r
5985 \*---------------------------------------------------------------------------*/\r
5986 void\r
5987 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5988 {\r
5989   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5990 \r
5991   while (*cd != NULL) {\r
5992     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5993     cd++;\r
5994   }\r
5995 }\r
5996 \r
5997 void\r
5998 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5999 {\r
6000   char buf1[MAX_ARG_LEN];\r
6001   int len;\r
6002 \r
6003   if (str[0] == '@') {\r
6004     FILE* f = fopen(str + 1, "r");\r
6005     if (f == NULL) {\r
6006       DisplayFatalError(str + 1, errno, 2);\r
6007       return;\r
6008     }\r
6009     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6010     fclose(f);\r
6011     buf1[len] = NULLCHAR;\r
6012     str = buf1;\r
6013   }\r
6014 \r
6015   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6016 \r
6017   for (;;) {\r
6018     char buf[MSG_SIZ];\r
6019     char *end = strchr(str, '\n');\r
6020     if (end == NULL) return;\r
6021     memcpy(buf, str, end - str);\r
6022     buf[end - str] = NULLCHAR;\r
6023     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6024     str = end + 1;\r
6025   }\r
6026 }\r
6027 \r
6028 void\r
6029 SetStartupDialogEnables(HWND hDlg)\r
6030 {\r
6031   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6032     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6033     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6034   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6035     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6036   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6037     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6038   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6039     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6040   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6041     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6042     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6043     IsDlgButtonChecked(hDlg, OPT_View));\r
6044 }\r
6045 \r
6046 char *\r
6047 QuoteForFilename(char *filename)\r
6048 {\r
6049   int dquote, space;\r
6050   dquote = strchr(filename, '"') != NULL;\r
6051   space = strchr(filename, ' ') != NULL;\r
6052   if (dquote || space) {\r
6053     if (dquote) {\r
6054       return "'";\r
6055     } else {\r
6056       return "\"";\r
6057     }\r
6058   } else {\r
6059     return "";\r
6060   }\r
6061 }\r
6062 \r
6063 VOID\r
6064 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6065 {\r
6066   char buf[MSG_SIZ];\r
6067   char *q;\r
6068 \r
6069   InitComboStringsFromOption(hwndCombo, nthnames);\r
6070   q = QuoteForFilename(nthcp);\r
6071     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6072   if (*nthdir != NULLCHAR) {\r
6073     q = QuoteForFilename(nthdir);\r
6074       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6075   }\r
6076   if (*nthcp == NULLCHAR) {\r
6077     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6078   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6079     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6080     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6081   }\r
6082 }\r
6083 \r
6084 LRESULT CALLBACK\r
6085 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6086 {\r
6087   char buf[MSG_SIZ];\r
6088   HANDLE hwndCombo;\r
6089   char *p;\r
6090 \r
6091   switch (message) {\r
6092   case WM_INITDIALOG:\r
6093     /* Center the dialog */\r
6094     CenterWindow (hDlg, GetDesktopWindow());\r
6095     Translate(hDlg, DLG_Startup);\r
6096     /* Initialize the dialog items */\r
6097     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6098                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6099                   firstChessProgramNames);\r
6100     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6101                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6102                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6103     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6104     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6105       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6106     if (*appData.icsHelper != NULLCHAR) {\r
6107       char *q = QuoteForFilename(appData.icsHelper);\r
6108       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6109     }\r
6110     if (*appData.icsHost == NULLCHAR) {\r
6111       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6112       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6113     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6114       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6115       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6116     }\r
6117 \r
6118     if (appData.icsActive) {\r
6119       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6120     }\r
6121     else if (appData.noChessProgram) {\r
6122       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6123     }\r
6124     else {\r
6125       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6126     }\r
6127 \r
6128     SetStartupDialogEnables(hDlg);\r
6129     return TRUE;\r
6130 \r
6131   case WM_COMMAND:\r
6132     switch (LOWORD(wParam)) {\r
6133     case IDOK:\r
6134       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6135         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6136         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6137         p = buf;\r
6138         ParseArgs(StringGet, &p);\r
6139         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6140         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6141         p = buf;
6142         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6143         ParseArgs(StringGet, &p);\r
6144         SwapEngines(singleList); // ... and then make it 'second'\r
6145         appData.noChessProgram = FALSE;\r
6146         appData.icsActive = FALSE;\r
6147       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6148         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6149         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6150         p = buf;\r
6151         ParseArgs(StringGet, &p);\r
6152         if (appData.zippyPlay) {\r
6153           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6154           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6155           p = buf;\r
6156           ParseArgs(StringGet, &p);\r
6157         }\r
6158       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6159         appData.noChessProgram = TRUE;\r
6160         appData.icsActive = FALSE;\r
6161       } else {\r
6162         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6163                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6164         return TRUE;\r
6165       }\r
6166       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6167         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6168         p = buf;\r
6169         ParseArgs(StringGet, &p);\r
6170       }\r
6171       EndDialog(hDlg, TRUE);\r
6172       return TRUE;\r
6173 \r
6174     case IDCANCEL:\r
6175       ExitEvent(0);\r
6176       return TRUE;\r
6177 \r
6178     case IDM_HELPCONTENTS:\r
6179       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6180         MessageBox (GetFocus(),\r
6181                     _("Unable to activate help"),\r
6182                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6183       }\r
6184       break;\r
6185 \r
6186     default:\r
6187       SetStartupDialogEnables(hDlg);\r
6188       break;\r
6189     }\r
6190     break;\r
6191   }\r
6192   return FALSE;\r
6193 }\r
6194 \r
6195 /*---------------------------------------------------------------------------*\\r
6196  *\r
6197  * About box dialog functions\r
6198  *\r
6199 \*---------------------------------------------------------------------------*/\r
6200 \r
6201 /* Process messages for "About" dialog box */\r
6202 LRESULT CALLBACK\r
6203 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6204 {\r
6205   switch (message) {\r
6206   case WM_INITDIALOG: /* message: initialize dialog box */\r
6207     /* Center the dialog over the application window */\r
6208     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6209     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6210     Translate(hDlg, ABOUTBOX);\r
6211     JAWS_COPYRIGHT\r
6212     return (TRUE);\r
6213 \r
6214   case WM_COMMAND: /* message: received a command */\r
6215     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6216         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6217       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6218       return (TRUE);\r
6219     }\r
6220     break;\r
6221   }\r
6222   return (FALSE);\r
6223 }\r
6224 \r
6225 /*---------------------------------------------------------------------------*\\r
6226  *\r
6227  * Comment Dialog functions\r
6228  *\r
6229 \*---------------------------------------------------------------------------*/\r
6230 \r
6231 LRESULT CALLBACK\r
6232 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6233 {\r
6234   static HANDLE hwndText = NULL;\r
6235   int len, newSizeX, newSizeY, flags;\r
6236   static int sizeX, sizeY;\r
6237   char *str;\r
6238   RECT rect;\r
6239   MINMAXINFO *mmi;\r
6240 \r
6241   switch (message) {\r
6242   case WM_INITDIALOG: /* message: initialize dialog box */\r
6243     /* Initialize the dialog items */\r
6244     Translate(hDlg, DLG_EditComment);\r
6245     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6246     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6247     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6248     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6249     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6250     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6251     SetWindowText(hDlg, commentTitle);\r
6252     if (editComment) {\r
6253       SetFocus(hwndText);\r
6254     } else {\r
6255       SetFocus(GetDlgItem(hDlg, IDOK));\r
6256     }\r
6257     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6258                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6259                 MAKELPARAM(FALSE, 0));\r
6260     /* Size and position the dialog */\r
6261     if (!commentDialog) {\r
6262       commentDialog = hDlg;\r
6263       flags = SWP_NOZORDER;\r
6264       GetClientRect(hDlg, &rect);\r
6265       sizeX = rect.right;\r
6266       sizeY = rect.bottom;\r
6267       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6268           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6269         WINDOWPLACEMENT wp;\r
6270         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6271         wp.length = sizeof(WINDOWPLACEMENT);\r
6272         wp.flags = 0;\r
6273         wp.showCmd = SW_SHOW;\r
6274         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6275         wp.rcNormalPosition.left = wpComment.x;\r
6276         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6277         wp.rcNormalPosition.top = wpComment.y;\r
6278         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6279         SetWindowPlacement(hDlg, &wp);\r
6280 \r
6281         GetClientRect(hDlg, &rect);\r
6282         newSizeX = rect.right;\r
6283         newSizeY = rect.bottom;\r
6284         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6285                               newSizeX, newSizeY);\r
6286         sizeX = newSizeX;\r
6287         sizeY = newSizeY;\r
6288       }\r
6289     }\r
6290     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6291     return FALSE;\r
6292 \r
6293   case WM_COMMAND: /* message: received a command */\r
6294     switch (LOWORD(wParam)) {\r
6295     case IDOK:\r
6296       if (editComment) {\r
6297         char *p, *q;\r
6298         /* Read changed options from the dialog box */\r
6299         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6300         len = GetWindowTextLength(hwndText);\r
6301         str = (char *) malloc(len + 1);\r
6302         GetWindowText(hwndText, str, len + 1);\r
6303         p = q = str;\r
6304         while (*q) {\r
6305           if (*q == '\r')\r
6306             q++;\r
6307           else\r
6308             *p++ = *q++;\r
6309         }\r
6310         *p = NULLCHAR;\r
6311         ReplaceComment(commentIndex, str);\r
6312         free(str);\r
6313       }\r
6314       CommentPopDown();\r
6315       return TRUE;\r
6316 \r
6317     case IDCANCEL:\r
6318     case OPT_CancelComment:\r
6319       CommentPopDown();\r
6320       return TRUE;\r
6321 \r
6322     case OPT_ClearComment:\r
6323       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6324       break;\r
6325 \r
6326     case OPT_EditComment:\r
6327       EditCommentEvent();\r
6328       return TRUE;\r
6329 \r
6330     default:\r
6331       break;\r
6332     }\r
6333     break;\r
6334 \r
6335   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6336         if( wParam == OPT_CommentText ) {\r
6337             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6338 \r
6339             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6340                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6341                 POINTL pt;\r
6342                 LRESULT index;\r
6343 \r
6344                 pt.x = LOWORD( lpMF->lParam );\r
6345                 pt.y = HIWORD( lpMF->lParam );\r
6346 \r
6347                 if(lpMF->msg == WM_CHAR) {\r
6348                         CHARRANGE sel;\r
6349                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6350                         index = sel.cpMin;\r
6351                 } else\r
6352                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6353 \r
6354                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6355                 len = GetWindowTextLength(hwndText);\r
6356                 str = (char *) malloc(len + 1);\r
6357                 GetWindowText(hwndText, str, len + 1);\r
6358                 ReplaceComment(commentIndex, str);\r
6359                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6360                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6361                 free(str);\r
6362 \r
6363                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6364                 lpMF->msg = WM_USER;\r
6365 \r
6366                 return TRUE;\r
6367             }\r
6368         }\r
6369         break;\r
6370 \r
6371   case WM_SIZE:\r
6372     newSizeX = LOWORD(lParam);\r
6373     newSizeY = HIWORD(lParam);\r
6374     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6375     sizeX = newSizeX;\r
6376     sizeY = newSizeY;\r
6377     break;\r
6378 \r
6379   case WM_GETMINMAXINFO:\r
6380     /* Prevent resizing window too small */\r
6381     mmi = (MINMAXINFO *) lParam;\r
6382     mmi->ptMinTrackSize.x = 100;\r
6383     mmi->ptMinTrackSize.y = 100;\r
6384     break;\r
6385   }\r
6386   return FALSE;\r
6387 }\r
6388 \r
6389 VOID\r
6390 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6391 {\r
6392   FARPROC lpProc;\r
6393   char *p, *q;\r
6394 \r
6395   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6396 \r
6397   if (str == NULL) str = "";\r
6398   p = (char *) malloc(2 * strlen(str) + 2);\r
6399   q = p;\r
6400   while (*str) {\r
6401     if (*str == '\n') *q++ = '\r';\r
6402     *q++ = *str++;\r
6403   }\r
6404   *q = NULLCHAR;\r
6405   if (commentText != NULL) free(commentText);\r
6406 \r
6407   commentIndex = index;\r
6408   commentTitle = title;\r
6409   commentText = p;\r
6410   editComment = edit;\r
6411 \r
6412   if (commentDialog) {\r
6413     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6414     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6415   } else {\r
6416     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6417     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6418                  hwndMain, (DLGPROC)lpProc);\r
6419     FreeProcInstance(lpProc);\r
6420   }\r
6421   commentUp = TRUE;\r
6422 }\r
6423 \r
6424 \r
6425 /*---------------------------------------------------------------------------*\\r
6426  *\r
6427  * Type-in move dialog functions\r
6428  * \r
6429 \*---------------------------------------------------------------------------*/\r
6430 \r
6431 LRESULT CALLBACK\r
6432 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6433 {\r
6434   char move[MSG_SIZ];\r
6435   HWND hInput;\r
6436 \r
6437   switch (message) {\r
6438   case WM_INITDIALOG:\r
6439     move[0] = (char) lParam;\r
6440     move[1] = NULLCHAR;\r
6441     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6442     Translate(hDlg, DLG_TypeInMove);\r
6443     hInput = GetDlgItem(hDlg, OPT_Move);\r
6444     SetWindowText(hInput, move);\r
6445     SetFocus(hInput);\r
6446     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6447     return FALSE;\r
6448 \r
6449   case WM_COMMAND:\r
6450     switch (LOWORD(wParam)) {\r
6451     case IDOK:
6452 \r
6453       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6454       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6455       TypeInDoneEvent(move);\r
6456       EndDialog(hDlg, TRUE);\r
6457       return TRUE;\r
6458     case IDCANCEL:\r
6459       EndDialog(hDlg, FALSE);\r
6460       return TRUE;\r
6461     default:\r
6462       break;\r
6463     }\r
6464     break;\r
6465   }\r
6466   return FALSE;\r
6467 }\r
6468 \r
6469 VOID\r
6470 PopUpMoveDialog(char firstchar)\r
6471 {\r
6472     FARPROC lpProc;\r
6473 \r
6474       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6475       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6476         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6477       FreeProcInstance(lpProc);\r
6478 }\r
6479 \r
6480 /*---------------------------------------------------------------------------*\\r
6481  *\r
6482  * Type-in name dialog functions\r
6483  * \r
6484 \*---------------------------------------------------------------------------*/\r
6485 \r
6486 LRESULT CALLBACK\r
6487 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6488 {\r
6489   char move[MSG_SIZ];\r
6490   HWND hInput;\r
6491 \r
6492   switch (message) {\r
6493   case WM_INITDIALOG:\r
6494     move[0] = (char) lParam;\r
6495     move[1] = NULLCHAR;\r
6496     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6497     Translate(hDlg, DLG_TypeInName);\r
6498     hInput = GetDlgItem(hDlg, OPT_Name);\r
6499     SetWindowText(hInput, move);\r
6500     SetFocus(hInput);\r
6501     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6502     return FALSE;\r
6503 \r
6504   case WM_COMMAND:\r
6505     switch (LOWORD(wParam)) {\r
6506     case IDOK:\r
6507       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6508       appData.userName = strdup(move);\r
6509       SetUserLogo();\r
6510       SetGameInfo();\r
6511       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6512         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6513         DisplayTitle(move);\r
6514       }\r
6515 \r
6516 \r
6517       EndDialog(hDlg, TRUE);\r
6518       return TRUE;\r
6519     case IDCANCEL:\r
6520       EndDialog(hDlg, FALSE);\r
6521       return TRUE;\r
6522     default:\r
6523       break;\r
6524     }\r
6525     break;\r
6526   }\r
6527   return FALSE;\r
6528 }\r
6529 \r
6530 VOID\r
6531 PopUpNameDialog(char firstchar)\r
6532 {\r
6533     FARPROC lpProc;\r
6534     \r
6535       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6536       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6537         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6538       FreeProcInstance(lpProc);\r
6539 }\r
6540 \r
6541 /*---------------------------------------------------------------------------*\\r
6542  *\r
6543  *  Error dialogs\r
6544  * \r
6545 \*---------------------------------------------------------------------------*/\r
6546 \r
6547 /* Nonmodal error box */\r
6548 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6549                              WPARAM wParam, LPARAM lParam);\r
6550 \r
6551 VOID\r
6552 ErrorPopUp(char *title, char *content)\r
6553 {\r
6554   FARPROC lpProc;\r
6555   char *p, *q;\r
6556   BOOLEAN modal = hwndMain == NULL;\r
6557 \r
6558   p = content;\r
6559   q = errorMessage;\r
6560   while (*p) {\r
6561     if (*p == '\n') {\r
6562       if (modal) {\r
6563         *q++ = ' ';\r
6564         p++;\r
6565       } else {\r
6566         *q++ = '\r';\r
6567         *q++ = *p++;\r
6568       }\r
6569     } else {\r
6570       *q++ = *p++;\r
6571     }\r
6572   }\r
6573   *q = NULLCHAR;\r
6574   strncpy(errorTitle, title, sizeof(errorTitle));\r
6575   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6576   \r
6577   if (modal) {\r
6578     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6579   } else {\r
6580     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6581     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6582                  hwndMain, (DLGPROC)lpProc);\r
6583     FreeProcInstance(lpProc);\r
6584   }\r
6585 }\r
6586 \r
6587 VOID\r
6588 ErrorPopDown()\r
6589 {\r
6590   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6591   if (errorDialog == NULL) return;\r
6592   DestroyWindow(errorDialog);\r
6593   errorDialog = NULL;\r
6594   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6595 }\r
6596 \r
6597 LRESULT CALLBACK\r
6598 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6599 {\r
6600   HANDLE hwndText;\r
6601   RECT rChild;\r
6602 \r
6603   switch (message) {\r
6604   case WM_INITDIALOG:\r
6605     GetWindowRect(hDlg, &rChild);\r
6606 \r
6607     /*\r
6608     SetWindowPos(hDlg, NULL, rChild.left,\r
6609       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6610       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6611     */\r
6612 \r
6613     /* \r
6614         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6615         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6616         and it doesn't work when you resize the dialog.\r
6617         For now, just give it a default position.\r
6618     */\r
6619     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6620     Translate(hDlg, DLG_Error);\r
6621 \r
6622     errorDialog = hDlg;\r
6623     SetWindowText(hDlg, errorTitle);\r
6624     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6625     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6626     return FALSE;\r
6627 \r
6628   case WM_COMMAND:\r
6629     switch (LOWORD(wParam)) {\r
6630     case IDOK:\r
6631     case IDCANCEL:\r
6632       if (errorDialog == hDlg) errorDialog = NULL;\r
6633       DestroyWindow(hDlg);\r
6634       return TRUE;\r
6635 \r
6636     default:\r
6637       break;\r
6638     }\r
6639     break;\r
6640   }\r
6641   return FALSE;\r
6642 }\r
6643 \r
6644 #ifdef GOTHIC\r
6645 HWND gothicDialog = NULL;\r
6646 \r
6647 LRESULT CALLBACK\r
6648 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6649 {\r
6650   HANDLE hwndText;\r
6651   RECT rChild;\r
6652   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6653 \r
6654   switch (message) {\r
6655   case WM_INITDIALOG:\r
6656     GetWindowRect(hDlg, &rChild);\r
6657 \r
6658     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6659                                                              SWP_NOZORDER);\r
6660 \r
6661     /* \r
6662         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6663         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6664         and it doesn't work when you resize the dialog.\r
6665         For now, just give it a default position.\r
6666     */\r
6667     gothicDialog = hDlg;\r
6668     SetWindowText(hDlg, errorTitle);\r
6669     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6670     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6671     return FALSE;\r
6672 \r
6673   case WM_COMMAND:\r
6674     switch (LOWORD(wParam)) {\r
6675     case IDOK:\r
6676     case IDCANCEL:\r
6677       if (errorDialog == hDlg) errorDialog = NULL;\r
6678       DestroyWindow(hDlg);\r
6679       return TRUE;\r
6680 \r
6681     default:\r
6682       break;\r
6683     }\r
6684     break;\r
6685   }\r
6686   return FALSE;\r
6687 }\r
6688 \r
6689 VOID\r
6690 GothicPopUp(char *title, VariantClass variant)\r
6691 {\r
6692   FARPROC lpProc;\r
6693   static char *lastTitle;\r
6694 \r
6695   strncpy(errorTitle, title, sizeof(errorTitle));\r
6696   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6697 \r
6698   if(lastTitle != title && gothicDialog != NULL) {\r
6699     DestroyWindow(gothicDialog);\r
6700     gothicDialog = NULL;\r
6701   }\r
6702   if(variant != VariantNormal && gothicDialog == NULL) {\r
6703     title = lastTitle;\r
6704     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6705     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6706                  hwndMain, (DLGPROC)lpProc);\r
6707     FreeProcInstance(lpProc);\r
6708   }\r
6709 }\r
6710 #endif\r
6711 \r
6712 /*---------------------------------------------------------------------------*\\r
6713  *\r
6714  *  Ics Interaction console functions\r
6715  *\r
6716 \*---------------------------------------------------------------------------*/\r
6717 \r
6718 #define HISTORY_SIZE 64\r
6719 static char *history[HISTORY_SIZE];\r
6720 int histIn = 0, histP = 0;\r
6721 \r
6722 VOID\r
6723 SaveInHistory(char *cmd)\r
6724 {\r
6725   if (history[histIn] != NULL) {\r
6726     free(history[histIn]);\r
6727     history[histIn] = NULL;\r
6728   }\r
6729   if (*cmd == NULLCHAR) return;\r
6730   history[histIn] = StrSave(cmd);\r
6731   histIn = (histIn + 1) % HISTORY_SIZE;\r
6732   if (history[histIn] != NULL) {\r
6733     free(history[histIn]);\r
6734     history[histIn] = NULL;\r
6735   }\r
6736   histP = histIn;\r
6737 }\r
6738 \r
6739 char *\r
6740 PrevInHistory(char *cmd)\r
6741 {\r
6742   int newhp;\r
6743   if (histP == histIn) {\r
6744     if (history[histIn] != NULL) free(history[histIn]);\r
6745     history[histIn] = StrSave(cmd);\r
6746   }\r
6747   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6748   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6749   histP = newhp;\r
6750   return history[histP];\r
6751 }\r
6752 \r
6753 char *\r
6754 NextInHistory()\r
6755 {\r
6756   if (histP == histIn) return NULL;\r
6757   histP = (histP + 1) % HISTORY_SIZE;\r
6758   return history[histP];   \r
6759 }\r
6760 \r
6761 HMENU\r
6762 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6763 {\r
6764   HMENU hmenu, h;\r
6765   int i = 0;\r
6766   hmenu = LoadMenu(hInst, "TextMenu");\r
6767   h = GetSubMenu(hmenu, 0);\r
6768   while (e->item) {\r
6769     if (strcmp(e->item, "-") == 0) {\r
6770       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6771     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6772       int flags = MF_STRING, j = 0;\r
6773       if (e->item[0] == '|') {\r
6774         flags |= MF_MENUBARBREAK;\r
6775         j++;\r
6776       }\r
6777       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6778       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6779     }\r
6780     e++;\r
6781     i++;\r
6782   } \r
6783   return hmenu;\r
6784 }\r
6785 \r
6786 WNDPROC consoleTextWindowProc;\r
6787 \r
6788 void\r
6789 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6790 {\r
6791   char buf[MSG_SIZ], name[MSG_SIZ];\r
6792   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6793   CHARRANGE sel;\r
6794 \r
6795   if (!getname) {\r
6796     SetWindowText(hInput, command);\r
6797     if (immediate) {\r
6798       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6799     } else {\r
6800       sel.cpMin = 999999;\r
6801       sel.cpMax = 999999;\r
6802       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6803       SetFocus(hInput);\r
6804     }\r
6805     return;\r
6806   }    \r
6807   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6808   if (sel.cpMin == sel.cpMax) {\r
6809     /* Expand to surrounding word */\r
6810     TEXTRANGE tr;\r
6811     do {\r
6812       tr.chrg.cpMax = sel.cpMin;\r
6813       tr.chrg.cpMin = --sel.cpMin;\r
6814       if (sel.cpMin < 0) break;\r
6815       tr.lpstrText = name;\r
6816       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6817     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6818     sel.cpMin++;\r
6819 \r
6820     do {\r
6821       tr.chrg.cpMin = sel.cpMax;\r
6822       tr.chrg.cpMax = ++sel.cpMax;\r
6823       tr.lpstrText = name;\r
6824       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6825     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6826     sel.cpMax--;\r
6827 \r
6828     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6829       MessageBeep(MB_ICONEXCLAMATION);\r
6830       return;\r
6831     }\r
6832     tr.chrg = sel;\r
6833     tr.lpstrText = name;\r
6834     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6835   } else {\r
6836     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6837       MessageBeep(MB_ICONEXCLAMATION);\r
6838       return;\r
6839     }\r
6840     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6841   }\r
6842   if (immediate) {\r
6843     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6844     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6845     SetWindowText(hInput, buf);\r
6846     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6847   } else {\r
6848     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6849       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6850     SetWindowText(hInput, buf);\r
6851     sel.cpMin = 999999;\r
6852     sel.cpMax = 999999;\r
6853     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6854     SetFocus(hInput);\r
6855   }\r
6856 }\r
6857 \r
6858 LRESULT CALLBACK \r
6859 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6860 {\r
6861   HWND hInput;\r
6862   CHARRANGE sel;\r
6863 \r
6864   switch (message) {\r
6865   case WM_KEYDOWN:\r
6866     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6867     if(wParam=='R') return 0;\r
6868     switch (wParam) {\r
6869     case VK_PRIOR:\r
6870       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6871       return 0;\r
6872     case VK_NEXT:\r
6873       sel.cpMin = 999999;\r
6874       sel.cpMax = 999999;\r
6875       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6876       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6877       return 0;\r
6878     }\r
6879     break;\r
6880   case WM_CHAR:\r
6881    if(wParam != '\022') {\r
6882     if (wParam == '\t') {\r
6883       if (GetKeyState(VK_SHIFT) < 0) {\r
6884         /* shifted */\r
6885         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6886         if (buttonDesc[0].hwnd) {\r
6887           SetFocus(buttonDesc[0].hwnd);\r
6888         } else {\r
6889           SetFocus(hwndMain);\r
6890         }\r
6891       } else {\r
6892         /* unshifted */\r
6893         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6894       }\r
6895     } else {\r
6896       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6897       JAWS_DELETE( SetFocus(hInput); )\r
6898       SendMessage(hInput, message, wParam, lParam);\r
6899     }\r
6900     return 0;\r
6901    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6902    lParam = -1;\r
6903   case WM_RBUTTONDOWN:\r
6904     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6905       /* Move selection here if it was empty */\r
6906       POINT pt;\r
6907       pt.x = LOWORD(lParam);\r
6908       pt.y = HIWORD(lParam);\r
6909       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6910       if (sel.cpMin == sel.cpMax) {\r
6911         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6912         sel.cpMax = sel.cpMin;\r
6913         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6914       }\r
6915       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6916 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6917       POINT pt;\r
6918       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6919       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6920       if (sel.cpMin == sel.cpMax) {\r
6921         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6922         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6923       }\r
6924       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6925         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6926       }\r
6927       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6928       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6929       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6930       MenuPopup(hwnd, pt, hmenu, -1);\r
6931 }\r
6932     }\r
6933     return 0;\r
6934   case WM_RBUTTONUP:\r
6935     if (GetKeyState(VK_SHIFT) & ~1) {\r
6936       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6937         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6938     }\r
6939     return 0;\r
6940   case WM_PASTE:\r
6941     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6942     SetFocus(hInput);\r
6943     return SendMessage(hInput, message, wParam, lParam);\r
6944   case WM_MBUTTONDOWN:\r
6945     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6946   case WM_COMMAND:\r
6947     switch (LOWORD(wParam)) {\r
6948     case IDM_QuickPaste:\r
6949       {\r
6950         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6951         if (sel.cpMin == sel.cpMax) {\r
6952           MessageBeep(MB_ICONEXCLAMATION);\r
6953           return 0;\r
6954         }\r
6955         SendMessage(hwnd, WM_COPY, 0, 0);\r
6956         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6957         SendMessage(hInput, WM_PASTE, 0, 0);\r
6958         SetFocus(hInput);\r
6959         return 0;\r
6960       }\r
6961     case IDM_Cut:\r
6962       SendMessage(hwnd, WM_CUT, 0, 0);\r
6963       return 0;\r
6964     case IDM_Paste:\r
6965       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6966       return 0;\r
6967     case IDM_Copy:\r
6968       SendMessage(hwnd, WM_COPY, 0, 0);\r
6969       return 0;\r
6970     default:\r
6971       {\r
6972         int i = LOWORD(wParam) - IDM_CommandX;\r
6973         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6974             icsTextMenuEntry[i].command != NULL) {\r
6975           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6976                    icsTextMenuEntry[i].getname,\r
6977                    icsTextMenuEntry[i].immediate);\r
6978           return 0;\r
6979         }\r
6980       }\r
6981       break;\r
6982     }\r
6983     break;\r
6984   }\r
6985   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6986 }\r
6987 \r
6988 WNDPROC consoleInputWindowProc;\r
6989 \r
6990 LRESULT CALLBACK\r
6991 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6992 {\r
6993   char buf[MSG_SIZ];\r
6994   char *p;\r
6995   static BOOL sendNextChar = FALSE;\r
6996   static BOOL quoteNextChar = FALSE;\r
6997   InputSource *is = consoleInputSource;\r
6998   CHARFORMAT cf;\r
6999   CHARRANGE sel;\r
7000 \r
7001   switch (message) {\r
7002   case WM_CHAR:\r
7003     if (!appData.localLineEditing || sendNextChar) {\r
7004       is->buf[0] = (CHAR) wParam;\r
7005       is->count = 1;\r
7006       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7007       sendNextChar = FALSE;\r
7008       return 0;\r
7009     }\r
7010     if (quoteNextChar) {\r
7011       buf[0] = (char) wParam;\r
7012       buf[1] = NULLCHAR;\r
7013       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7014       quoteNextChar = FALSE;\r
7015       return 0;\r
7016     }\r
7017     switch (wParam) {\r
7018     case '\r':   /* Enter key */\r
7019       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7020       if (consoleEcho) SaveInHistory(is->buf);\r
7021       is->buf[is->count++] = '\n';\r
7022       is->buf[is->count] = NULLCHAR;\r
7023       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7024       if (consoleEcho) {\r
7025         ConsoleOutput(is->buf, is->count, TRUE);\r
7026       } else if (appData.localLineEditing) {\r
7027         ConsoleOutput("\n", 1, TRUE);\r
7028       }\r
7029       /* fall thru */\r
7030     case '\033': /* Escape key */\r
7031       SetWindowText(hwnd, "");\r
7032       cf.cbSize = sizeof(CHARFORMAT);\r
7033       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7034       if (consoleEcho) {\r
7035         cf.crTextColor = textAttribs[ColorNormal].color;\r
7036       } else {\r
7037         cf.crTextColor = COLOR_ECHOOFF;\r
7038       }\r
7039       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7040       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7041       return 0;\r
7042     case '\t':   /* Tab key */\r
7043       if (GetKeyState(VK_SHIFT) < 0) {\r
7044         /* shifted */\r
7045         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7046       } else {\r
7047         /* unshifted */\r
7048         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7049         if (buttonDesc[0].hwnd) {\r
7050           SetFocus(buttonDesc[0].hwnd);\r
7051         } else {\r
7052           SetFocus(hwndMain);\r
7053         }\r
7054       }\r
7055       return 0;\r
7056     case '\023': /* Ctrl+S */\r
7057       sendNextChar = TRUE;\r
7058       return 0;\r
7059     case '\021': /* Ctrl+Q */\r
7060       quoteNextChar = TRUE;\r
7061       return 0;\r
7062     JAWS_REPLAY\r
7063     default:\r
7064       break;\r
7065     }\r
7066     break;\r
7067   case WM_KEYDOWN:\r
7068     switch (wParam) {\r
7069     case VK_UP:\r
7070       GetWindowText(hwnd, buf, MSG_SIZ);\r
7071       p = PrevInHistory(buf);\r
7072       if (p != NULL) {\r
7073         SetWindowText(hwnd, p);\r
7074         sel.cpMin = 999999;\r
7075         sel.cpMax = 999999;\r
7076         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7077         return 0;\r
7078       }\r
7079       break;\r
7080     case VK_DOWN:\r
7081       p = NextInHistory();\r
7082       if (p != NULL) {\r
7083         SetWindowText(hwnd, p);\r
7084         sel.cpMin = 999999;\r
7085         sel.cpMax = 999999;\r
7086         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7087         return 0;\r
7088       }\r
7089       break;\r
7090     case VK_HOME:\r
7091     case VK_END:\r
7092       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7093       /* fall thru */\r
7094     case VK_PRIOR:\r
7095     case VK_NEXT:\r
7096       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7097       return 0;\r
7098     }\r
7099     break;\r
7100   case WM_MBUTTONDOWN:\r
7101     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7102       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7103     break;\r
7104   case WM_RBUTTONUP:\r
7105     if (GetKeyState(VK_SHIFT) & ~1) {\r
7106       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7107         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7108     } else {\r
7109       POINT pt;\r
7110       HMENU hmenu;\r
7111       hmenu = LoadMenu(hInst, "InputMenu");\r
7112       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7113       if (sel.cpMin == sel.cpMax) {\r
7114         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7115         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7116       }\r
7117       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7118         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7119       }\r
7120       pt.x = LOWORD(lParam);\r
7121       pt.y = HIWORD(lParam);\r
7122       MenuPopup(hwnd, pt, hmenu, -1);\r
7123     }\r
7124     return 0;\r
7125   case WM_COMMAND:\r
7126     switch (LOWORD(wParam)) { \r
7127     case IDM_Undo:\r
7128       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7129       return 0;\r
7130     case IDM_SelectAll:\r
7131       sel.cpMin = 0;\r
7132       sel.cpMax = -1; /*999999?*/\r
7133       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7134       return 0;\r
7135     case IDM_Cut:\r
7136       SendMessage(hwnd, WM_CUT, 0, 0);\r
7137       return 0;\r
7138     case IDM_Paste:\r
7139       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7140       return 0;\r
7141     case IDM_Copy:\r
7142       SendMessage(hwnd, WM_COPY, 0, 0);\r
7143       return 0;\r
7144     }\r
7145     break;\r
7146   }\r
7147   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7148 }\r
7149 \r
7150 #define CO_MAX  100000\r
7151 #define CO_TRIM   1000\r
7152 \r
7153 LRESULT CALLBACK\r
7154 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7155 {\r
7156   static SnapData sd;\r
7157   HWND hText, hInput;\r
7158   RECT rect;\r
7159   static int sizeX, sizeY;\r
7160   int newSizeX, newSizeY;\r
7161   MINMAXINFO *mmi;\r
7162   WORD wMask;\r
7163 \r
7164   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7165   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7166 \r
7167   switch (message) {\r
7168   case WM_NOTIFY:\r
7169     if (((NMHDR*)lParam)->code == EN_LINK)\r
7170     {\r
7171       ENLINK *pLink = (ENLINK*)lParam;\r
7172       if (pLink->msg == WM_LBUTTONUP)\r
7173       {\r
7174         TEXTRANGE tr;\r
7175 \r
7176         tr.chrg = pLink->chrg;\r
7177         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7178         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7179         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7180         free(tr.lpstrText);\r
7181       }\r
7182     }\r
7183     break;\r
7184   case WM_INITDIALOG: /* message: initialize dialog box */\r
7185     hwndConsole = hDlg;\r
7186     SetFocus(hInput);\r
7187     consoleTextWindowProc = (WNDPROC)\r
7188       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7189     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7190     consoleInputWindowProc = (WNDPROC)\r
7191       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7192     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7193     Colorize(ColorNormal, TRUE);\r
7194     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7195     ChangedConsoleFont();\r
7196     GetClientRect(hDlg, &rect);\r
7197     sizeX = rect.right;\r
7198     sizeY = rect.bottom;\r
7199     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7200         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7201       WINDOWPLACEMENT wp;\r
7202       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7203       wp.length = sizeof(WINDOWPLACEMENT);\r
7204       wp.flags = 0;\r
7205       wp.showCmd = SW_SHOW;\r
7206       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7207       wp.rcNormalPosition.left = wpConsole.x;\r
7208       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7209       wp.rcNormalPosition.top = wpConsole.y;\r
7210       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7211       SetWindowPlacement(hDlg, &wp);\r
7212     }\r
7213 \r
7214    // [HGM] Chessknight's change 2004-07-13\r
7215    else { /* Determine Defaults */\r
7216        WINDOWPLACEMENT wp;\r
7217        wpConsole.x = wpMain.width + 1;\r
7218        wpConsole.y = wpMain.y;\r
7219        wpConsole.width = screenWidth -  wpMain.width;\r
7220        wpConsole.height = wpMain.height;\r
7221        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7222        wp.length = sizeof(WINDOWPLACEMENT);\r
7223        wp.flags = 0;\r
7224        wp.showCmd = SW_SHOW;\r
7225        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7226        wp.rcNormalPosition.left = wpConsole.x;\r
7227        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7228        wp.rcNormalPosition.top = wpConsole.y;\r
7229        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7230        SetWindowPlacement(hDlg, &wp);\r
7231     }\r
7232 \r
7233    // Allow hText to highlight URLs and send notifications on them\r
7234    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7235    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7236    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7237    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7238 \r
7239     return FALSE;\r
7240 \r
7241   case WM_SETFOCUS:\r
7242     SetFocus(hInput);\r
7243     return 0;\r
7244 \r
7245   case WM_CLOSE:\r
7246     ExitEvent(0);\r
7247     /* not reached */\r
7248     break;\r
7249 \r
7250   case WM_SIZE:\r
7251     if (IsIconic(hDlg)) break;\r
7252     newSizeX = LOWORD(lParam);\r
7253     newSizeY = HIWORD(lParam);\r
7254     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7255       RECT rectText, rectInput;\r
7256       POINT pt;\r
7257       int newTextHeight, newTextWidth;\r
7258       GetWindowRect(hText, &rectText);\r
7259       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7260       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7261       if (newTextHeight < 0) {\r
7262         newSizeY += -newTextHeight;\r
7263         newTextHeight = 0;\r
7264       }\r
7265       SetWindowPos(hText, NULL, 0, 0,\r
7266         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7267       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7268       pt.x = rectInput.left;\r
7269       pt.y = rectInput.top + newSizeY - sizeY;\r
7270       ScreenToClient(hDlg, &pt);\r
7271       SetWindowPos(hInput, NULL, \r
7272         pt.x, pt.y, /* needs client coords */   \r
7273         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7274         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7275     }\r
7276     sizeX = newSizeX;\r
7277     sizeY = newSizeY;\r
7278     break;\r
7279 \r
7280   case WM_GETMINMAXINFO:\r
7281     /* Prevent resizing window too small */\r
7282     mmi = (MINMAXINFO *) lParam;\r
7283     mmi->ptMinTrackSize.x = 100;\r
7284     mmi->ptMinTrackSize.y = 100;\r
7285     break;\r
7286 \r
7287   /* [AS] Snapping */\r
7288   case WM_ENTERSIZEMOVE:\r
7289     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7290 \r
7291   case WM_SIZING:\r
7292     return OnSizing( &sd, hDlg, wParam, lParam );\r
7293 \r
7294   case WM_MOVING:\r
7295     return OnMoving( &sd, hDlg, wParam, lParam );\r
7296 \r
7297   case WM_EXITSIZEMOVE:\r
7298         UpdateICSWidth(hText);\r
7299     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7300   }\r
7301 \r
7302   return DefWindowProc(hDlg, message, wParam, lParam);\r
7303 }\r
7304 \r
7305 \r
7306 VOID\r
7307 ConsoleCreate()\r
7308 {\r
7309   HWND hCons;\r
7310   if (hwndConsole) return;\r
7311   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7312   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7313 }\r
7314 \r
7315 \r
7316 VOID\r
7317 ConsoleOutput(char* data, int length, int forceVisible)\r
7318 {\r
7319   HWND hText;\r
7320   int trim, exlen;\r
7321   char *p, *q;\r
7322   char buf[CO_MAX+1];\r
7323   POINT pEnd;\r
7324   RECT rect;\r
7325   static int delayLF = 0;\r
7326   CHARRANGE savesel, sel;\r
7327 \r
7328   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7329   p = data;\r
7330   q = buf;\r
7331   if (delayLF) {\r
7332     *q++ = '\r';\r
7333     *q++ = '\n';\r
7334     delayLF = 0;\r
7335   }\r
7336   while (length--) {\r
7337     if (*p == '\n') {\r
7338       if (*++p) {\r
7339         *q++ = '\r';\r
7340         *q++ = '\n';\r
7341       } else {\r
7342         delayLF = 1;\r
7343       }\r
7344     } else if (*p == '\007') {\r
7345        MyPlaySound(&sounds[(int)SoundBell]);\r
7346        p++;\r
7347     } else {\r
7348       *q++ = *p++;\r
7349     }\r
7350   }\r
7351   *q = NULLCHAR;\r
7352   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7353   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7354   /* Save current selection */\r
7355   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7356   exlen = GetWindowTextLength(hText);\r
7357   /* Find out whether current end of text is visible */\r
7358   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7359   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7360   /* Trim existing text if it's too long */\r
7361   if (exlen + (q - buf) > CO_MAX) {\r
7362     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7363     sel.cpMin = 0;\r
7364     sel.cpMax = trim;\r
7365     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7366     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7367     exlen -= trim;\r
7368     savesel.cpMin -= trim;\r
7369     savesel.cpMax -= trim;\r
7370     if (exlen < 0) exlen = 0;\r
7371     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7372     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7373   }\r
7374   /* Append the new text */\r
7375   sel.cpMin = exlen;\r
7376   sel.cpMax = exlen;\r
7377   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7378   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7379   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7380   if (forceVisible || exlen == 0 ||\r
7381       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7382        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7383     /* Scroll to make new end of text visible if old end of text\r
7384        was visible or new text is an echo of user typein */\r
7385     sel.cpMin = 9999999;\r
7386     sel.cpMax = 9999999;\r
7387     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7388     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7389     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7390     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7391   }\r
7392   if (savesel.cpMax == exlen || forceVisible) {\r
7393     /* Move insert point to new end of text if it was at the old\r
7394        end of text or if the new text is an echo of user typein */\r
7395     sel.cpMin = 9999999;\r
7396     sel.cpMax = 9999999;\r
7397     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7398   } else {\r
7399     /* Restore previous selection */\r
7400     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7401   }\r
7402   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7403 }\r
7404 \r
7405 /*---------*/\r
7406 \r
7407 \r
7408 void\r
7409 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7410 {\r
7411   char buf[100];\r
7412   char *str;\r
7413   COLORREF oldFg, oldBg;\r
7414   HFONT oldFont;\r
7415   RECT rect;\r
7416 \r
7417   if(copyNumber > 1)
7418     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7419 \r
7420   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7421   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7422   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7423 \r
7424   rect.left = x;\r
7425   rect.right = x + squareSize;\r
7426   rect.top  = y;\r
7427   rect.bottom = y + squareSize;\r
7428   str = buf;\r
7429 \r
7430   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7431                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7432              y, ETO_CLIPPED|ETO_OPAQUE,\r
7433              &rect, str, strlen(str), NULL);\r
7434 \r
7435   (void) SetTextColor(hdc, oldFg);\r
7436   (void) SetBkColor(hdc, oldBg);\r
7437   (void) SelectObject(hdc, oldFont);\r
7438 }\r
7439 \r
7440 void\r
7441 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7442               RECT *rect, char *color, char *flagFell)\r
7443 {\r
7444   char buf[100];\r
7445   char *str;\r
7446   COLORREF oldFg, oldBg;\r
7447   HFONT oldFont;\r
7448 \r
7449   if (appData.clockMode) {\r
7450     if (tinyLayout)\r
7451       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7452     else\r
7453       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7454     str = buf;\r
7455   } else {\r
7456     str = color;\r
7457   }\r
7458 \r
7459   if (highlight) {\r
7460     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7461     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7462   } else {\r
7463     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7464     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7465   }\r
7466   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7467 \r
7468   JAWS_SILENCE\r
7469 \r
7470   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7471              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7472              rect, str, strlen(str), NULL);\r
7473   if(logoHeight > 0 && appData.clockMode) {\r
7474       RECT r;\r
7475       str += strlen(color)+2;\r
7476       r.top = rect->top + logoHeight/2;\r
7477       r.left = rect->left;\r
7478       r.right = rect->right;\r
7479       r.bottom = rect->bottom;\r
7480       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7481                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7482                  &r, str, strlen(str), NULL);\r
7483   }\r
7484   (void) SetTextColor(hdc, oldFg);\r
7485   (void) SetBkColor(hdc, oldBg);\r
7486   (void) SelectObject(hdc, oldFont);\r
7487 }\r
7488 \r
7489 \r
7490 int\r
7491 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7492            OVERLAPPED *ovl)\r
7493 {\r
7494   int ok, err;\r
7495 \r
7496   /* [AS]  */\r
7497   if( count <= 0 ) {\r
7498     if (appData.debugMode) {\r
7499       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7500     }\r
7501 \r
7502     return ERROR_INVALID_USER_BUFFER;\r
7503   }\r
7504 \r
7505   ResetEvent(ovl->hEvent);\r
7506   ovl->Offset = ovl->OffsetHigh = 0;\r
7507   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7508   if (ok) {\r
7509     err = NO_ERROR;\r
7510   } else {\r
7511     err = GetLastError();\r
7512     if (err == ERROR_IO_PENDING) {\r
7513       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7514       if (ok)\r
7515         err = NO_ERROR;\r
7516       else\r
7517         err = GetLastError();\r
7518     }\r
7519   }\r
7520   return err;\r
7521 }\r
7522 \r
7523 int\r
7524 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7525             OVERLAPPED *ovl)\r
7526 {\r
7527   int ok, err;\r
7528 \r
7529   ResetEvent(ovl->hEvent);\r
7530   ovl->Offset = ovl->OffsetHigh = 0;\r
7531   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7532   if (ok) {\r
7533     err = NO_ERROR;\r
7534   } else {\r
7535     err = GetLastError();\r
7536     if (err == ERROR_IO_PENDING) {\r
7537       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7538       if (ok)\r
7539         err = NO_ERROR;\r
7540       else\r
7541         err = GetLastError();\r
7542     }\r
7543   }\r
7544   return err;\r
7545 }\r
7546 \r
7547 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7548 void CheckForInputBufferFull( InputSource * is )\r
7549 {\r
7550     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7551         /* Look for end of line */\r
7552         char * p = is->buf;\r
7553         \r
7554         while( p < is->next && *p != '\n' ) {\r
7555             p++;\r
7556         }\r
7557 \r
7558         if( p >= is->next ) {\r
7559             if (appData.debugMode) {\r
7560                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7561             }\r
7562 \r
7563             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7564             is->count = (DWORD) -1;\r
7565             is->next = is->buf;\r
7566         }\r
7567     }\r
7568 }\r
7569 \r
7570 DWORD\r
7571 InputThread(LPVOID arg)\r
7572 {\r
7573   InputSource *is;\r
7574   OVERLAPPED ovl;\r
7575 \r
7576   is = (InputSource *) arg;\r
7577   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7578   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7579   while (is->hThread != NULL) {\r
7580     is->error = DoReadFile(is->hFile, is->next,\r
7581                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7582                            &is->count, &ovl);\r
7583     if (is->error == NO_ERROR) {\r
7584       is->next += is->count;\r
7585     } else {\r
7586       if (is->error == ERROR_BROKEN_PIPE) {\r
7587         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7588         is->count = 0;\r
7589       } else {\r
7590         is->count = (DWORD) -1;\r
7591         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7592         break; \r
7593       }\r
7594     }\r
7595 \r
7596     CheckForInputBufferFull( is );\r
7597 \r
7598     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7599 \r
7600     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7601 \r
7602     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7603   }\r
7604 \r
7605   CloseHandle(ovl.hEvent);\r
7606   CloseHandle(is->hFile);\r
7607 \r
7608   if (appData.debugMode) {\r
7609     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7610   }\r
7611 \r
7612   return 0;\r
7613 }\r
7614 \r
7615 \r
7616 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7617 DWORD\r
7618 NonOvlInputThread(LPVOID arg)\r
7619 {\r
7620   InputSource *is;\r
7621   char *p, *q;\r
7622   int i;\r
7623   char prev;\r
7624 \r
7625   is = (InputSource *) arg;\r
7626   while (is->hThread != NULL) {\r
7627     is->error = ReadFile(is->hFile, is->next,\r
7628                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7629                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7630     if (is->error == NO_ERROR) {\r
7631       /* Change CRLF to LF */\r
7632       if (is->next > is->buf) {\r
7633         p = is->next - 1;\r
7634         i = is->count + 1;\r
7635       } else {\r
7636         p = is->next;\r
7637         i = is->count;\r
7638       }\r
7639       q = p;\r
7640       prev = NULLCHAR;\r
7641       while (i > 0) {\r
7642         if (prev == '\r' && *p == '\n') {\r
7643           *(q-1) = '\n';\r
7644           is->count--;\r
7645         } else { \r
7646           *q++ = *p;\r
7647         }\r
7648         prev = *p++;\r
7649         i--;\r
7650       }\r
7651       *q = NULLCHAR;\r
7652       is->next = q;\r
7653     } else {\r
7654       if (is->error == ERROR_BROKEN_PIPE) {\r
7655         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7656         is->count = 0; \r
7657       } else {\r
7658         is->count = (DWORD) -1;\r
7659       }\r
7660     }\r
7661 \r
7662     CheckForInputBufferFull( is );\r
7663 \r
7664     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7665 \r
7666     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7667 \r
7668     if (is->count < 0) break;  /* Quit on error */\r
7669   }\r
7670   CloseHandle(is->hFile);\r
7671   return 0;\r
7672 }\r
7673 \r
7674 DWORD\r
7675 SocketInputThread(LPVOID arg)\r
7676 {\r
7677   InputSource *is;\r
7678 \r
7679   is = (InputSource *) arg;\r
7680   while (is->hThread != NULL) {\r
7681     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7682     if ((int)is->count == SOCKET_ERROR) {\r
7683       is->count = (DWORD) -1;\r
7684       is->error = WSAGetLastError();\r
7685     } else {\r
7686       is->error = NO_ERROR;\r
7687       is->next += is->count;\r
7688       if (is->count == 0 && is->second == is) {\r
7689         /* End of file on stderr; quit with no message */\r
7690         break;\r
7691       }\r
7692     }\r
7693     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7694 \r
7695     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7696 \r
7697     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7698   }\r
7699   return 0;\r
7700 }\r
7701 \r
7702 VOID\r
7703 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7704 {\r
7705   InputSource *is;\r
7706 \r
7707   is = (InputSource *) lParam;\r
7708   if (is->lineByLine) {\r
7709     /* Feed in lines one by one */\r
7710     char *p = is->buf;\r
7711     char *q = p;\r
7712     while (q < is->next) {\r
7713       if (*q++ == '\n') {\r
7714         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7715         p = q;\r
7716       }\r
7717     }\r
7718     \r
7719     /* Move any partial line to the start of the buffer */\r
7720     q = is->buf;\r
7721     while (p < is->next) {\r
7722       *q++ = *p++;\r
7723     }\r
7724     is->next = q;\r
7725 \r
7726     if (is->error != NO_ERROR || is->count == 0) {\r
7727       /* Notify backend of the error.  Note: If there was a partial\r
7728          line at the end, it is not flushed through. */\r
7729       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7730     }\r
7731   } else {\r
7732     /* Feed in the whole chunk of input at once */\r
7733     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7734     is->next = is->buf;\r
7735   }\r
7736 }\r
7737 \r
7738 /*---------------------------------------------------------------------------*\\r
7739  *\r
7740  *  Menu enables. Used when setting various modes.\r
7741  *\r
7742 \*---------------------------------------------------------------------------*/\r
7743 \r
7744 typedef struct {\r
7745   int item;\r
7746   int flags;\r
7747 } Enables;\r
7748 \r
7749 VOID\r
7750 GreyRevert(Boolean grey)\r
7751 { // [HGM] vari: for retracting variations in local mode\r
7752   HMENU hmenu = GetMenu(hwndMain);\r
7753   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7754   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7755 }\r
7756 \r
7757 VOID\r
7758 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7759 {\r
7760   while (enab->item > 0) {\r
7761     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7762     enab++;\r
7763   }\r
7764 }\r
7765 \r
7766 Enables gnuEnables[] = {\r
7767   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7770   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7771   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7780 \r
7781   // Needed to switch from ncp to GNU mode on Engine Load\r
7782   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7783   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7784   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7785   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7786   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7787   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7788   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7789   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7790   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7791   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7792   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7793   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7794   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7795   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7796   { -1, -1 }\r
7797 };\r
7798 \r
7799 Enables icsEnables[] = {\r
7800   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7808   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7809   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7810   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7811   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7812   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7813   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7815   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7819   { -1, -1 }\r
7820 };\r
7821 \r
7822 #if ZIPPY\r
7823 Enables zippyEnables[] = {\r
7824   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7825   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7826   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7827   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7828   { -1, -1 }\r
7829 };\r
7830 #endif\r
7831 \r
7832 Enables ncpEnables[] = {\r
7833   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7842   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7843   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7855   { -1, -1 }\r
7856 };\r
7857 \r
7858 Enables trainingOnEnables[] = {\r
7859   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7867   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7868   { -1, -1 }\r
7869 };\r
7870 \r
7871 Enables trainingOffEnables[] = {\r
7872   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7877   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7881   { -1, -1 }\r
7882 };\r
7883 \r
7884 /* These modify either ncpEnables or gnuEnables */\r
7885 Enables cmailEnables[] = {\r
7886   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7887   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7888   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7889   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7891   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7893   { -1, -1 }\r
7894 };\r
7895 \r
7896 Enables machineThinkingEnables[] = {\r
7897   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7910 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7913   { -1, -1 }\r
7914 };\r
7915 \r
7916 Enables userThinkingEnables[] = {\r
7917   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7928   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7929   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7930 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7931   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7932   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7933   { -1, -1 }\r
7934 };\r
7935 \r
7936 /*---------------------------------------------------------------------------*\\r
7937  *\r
7938  *  Front-end interface functions exported by XBoard.\r
7939  *  Functions appear in same order as prototypes in frontend.h.\r
7940  * \r
7941 \*---------------------------------------------------------------------------*/\r
7942 VOID\r
7943 CheckMark(UINT item, int state)\r
7944 {\r
7945     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7946 }\r
7947 \r
7948 VOID\r
7949 ModeHighlight()\r
7950 {\r
7951   static UINT prevChecked = 0;\r
7952   static int prevPausing = 0;\r
7953   UINT nowChecked;\r
7954 \r
7955   if (pausing != prevPausing) {\r
7956     prevPausing = pausing;\r
7957     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7958                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7959     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7960   }\r
7961 \r
7962   switch (gameMode) {\r
7963   case BeginningOfGame:\r
7964     if (appData.icsActive)\r
7965       nowChecked = IDM_IcsClient;\r
7966     else if (appData.noChessProgram)\r
7967       nowChecked = IDM_EditGame;\r
7968     else\r
7969       nowChecked = IDM_MachineBlack;\r
7970     break;\r
7971   case MachinePlaysBlack:\r
7972     nowChecked = IDM_MachineBlack;\r
7973     break;\r
7974   case MachinePlaysWhite:\r
7975     nowChecked = IDM_MachineWhite;\r
7976     break;\r
7977   case TwoMachinesPlay:\r
7978     nowChecked = IDM_TwoMachines;\r
7979     break;\r
7980   case AnalyzeMode:\r
7981     nowChecked = IDM_AnalysisMode;\r
7982     break;\r
7983   case AnalyzeFile:\r
7984     nowChecked = IDM_AnalyzeFile;\r
7985     break;\r
7986   case EditGame:\r
7987     nowChecked = IDM_EditGame;\r
7988     break;\r
7989   case PlayFromGameFile:\r
7990     nowChecked = IDM_LoadGame;\r
7991     break;\r
7992   case EditPosition:\r
7993     nowChecked = IDM_EditPosition;\r
7994     break;\r
7995   case Training:\r
7996     nowChecked = IDM_Training;\r
7997     break;\r
7998   case IcsPlayingWhite:\r
7999   case IcsPlayingBlack:\r
8000   case IcsObserving:\r
8001   case IcsIdle:\r
8002     nowChecked = IDM_IcsClient;\r
8003     break;\r
8004   default:\r
8005   case EndOfGame:\r
8006     nowChecked = 0;\r
8007     break;\r
8008   }\r
8009   CheckMark(prevChecked, MF_UNCHECKED);\r
8010   CheckMark(nowChecked, MF_CHECKED);\r
8011   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8012 \r
8013   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8014     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8015                           MF_BYCOMMAND|MF_ENABLED);\r
8016   } else {\r
8017     (void) EnableMenuItem(GetMenu(hwndMain), \r
8018                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8019   }\r
8020 \r
8021   prevChecked = nowChecked;\r
8022 \r
8023   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8024   if (appData.icsActive) {\r
8025        if (appData.icsEngineAnalyze) {\r
8026                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8027        } else {\r
8028                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8029        }\r
8030   }\r
8031   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8032 }\r
8033 \r
8034 VOID\r
8035 SetICSMode()\r
8036 {\r
8037   HMENU hmenu = GetMenu(hwndMain);\r
8038   SetMenuEnables(hmenu, icsEnables);\r
8039   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8040     MF_BYCOMMAND|MF_ENABLED);\r
8041 #if ZIPPY\r
8042   if (appData.zippyPlay) {\r
8043     SetMenuEnables(hmenu, zippyEnables);\r
8044     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8045          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8046           MF_BYCOMMAND|MF_ENABLED);\r
8047   }\r
8048 #endif\r
8049 }\r
8050 \r
8051 VOID\r
8052 SetGNUMode()\r
8053 {\r
8054   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8055 }\r
8056 \r
8057 VOID\r
8058 SetNCPMode()\r
8059 {\r
8060   HMENU hmenu = GetMenu(hwndMain);\r
8061   SetMenuEnables(hmenu, ncpEnables);\r
8062     DrawMenuBar(hwndMain);\r
8063 }\r
8064 \r
8065 VOID\r
8066 SetCmailMode()\r
8067 {\r
8068   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8069 }\r
8070 \r
8071 VOID \r
8072 SetTrainingModeOn()\r
8073 {\r
8074   int i;\r
8075   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8076   for (i = 0; i < N_BUTTONS; i++) {\r
8077     if (buttonDesc[i].hwnd != NULL)\r
8078       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8079   }\r
8080   CommentPopDown();\r
8081 }\r
8082 \r
8083 VOID SetTrainingModeOff()\r
8084 {\r
8085   int i;\r
8086   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8087   for (i = 0; i < N_BUTTONS; i++) {\r
8088     if (buttonDesc[i].hwnd != NULL)\r
8089       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8090   }\r
8091 }\r
8092 \r
8093 \r
8094 VOID\r
8095 SetUserThinkingEnables()\r
8096 {\r
8097   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8098 }\r
8099 \r
8100 VOID\r
8101 SetMachineThinkingEnables()\r
8102 {\r
8103   HMENU hMenu = GetMenu(hwndMain);\r
8104   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8105 \r
8106   SetMenuEnables(hMenu, machineThinkingEnables);\r
8107 \r
8108   if (gameMode == MachinePlaysBlack) {\r
8109     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8110   } else if (gameMode == MachinePlaysWhite) {\r
8111     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8112   } else if (gameMode == TwoMachinesPlay) {\r
8113     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8114   }\r
8115 }\r
8116 \r
8117 \r
8118 VOID\r
8119 DisplayTitle(char *str)\r
8120 {\r
8121   char title[MSG_SIZ], *host;\r
8122   if (str[0] != NULLCHAR) {\r
8123     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8124   } else if (appData.icsActive) {\r
8125     if (appData.icsCommPort[0] != NULLCHAR)\r
8126       host = "ICS";\r
8127     else \r
8128       host = appData.icsHost;\r
8129       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8130   } else if (appData.noChessProgram) {\r
8131     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8132   } else {\r
8133     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8134     strcat(title, ": ");\r
8135     strcat(title, first.tidy);\r
8136   }\r
8137   SetWindowText(hwndMain, title);\r
8138 }\r
8139 \r
8140 \r
8141 VOID\r
8142 DisplayMessage(char *str1, char *str2)\r
8143 {\r
8144   HDC hdc;\r
8145   HFONT oldFont;\r
8146   int remain = MESSAGE_TEXT_MAX - 1;\r
8147   int len;\r
8148 \r
8149   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8150   messageText[0] = NULLCHAR;\r
8151   if (*str1) {\r
8152     len = strlen(str1);\r
8153     if (len > remain) len = remain;\r
8154     strncpy(messageText, str1, len);\r
8155     messageText[len] = NULLCHAR;\r
8156     remain -= len;\r
8157   }\r
8158   if (*str2 && remain >= 2) {\r
8159     if (*str1) {\r
8160       strcat(messageText, "  ");\r
8161       remain -= 2;\r
8162     }\r
8163     len = strlen(str2);\r
8164     if (len > remain) len = remain;\r
8165     strncat(messageText, str2, len);\r
8166   }\r
8167   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8168   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8169 \r
8170   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8171 \r
8172   SAYMACHINEMOVE();\r
8173 \r
8174   hdc = GetDC(hwndMain);\r
8175   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8176   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8177              &messageRect, messageText, strlen(messageText), NULL);\r
8178   (void) SelectObject(hdc, oldFont);\r
8179   (void) ReleaseDC(hwndMain, hdc);\r
8180 }\r
8181 \r
8182 VOID\r
8183 DisplayError(char *str, int error)\r
8184 {\r
8185   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8186   int len;\r
8187 \r
8188   if (error == 0) {\r
8189     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8190   } else {\r
8191     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8192                         NULL, error, LANG_NEUTRAL,\r
8193                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8194     if (len > 0) {\r
8195       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8196     } else {\r
8197       ErrorMap *em = errmap;\r
8198       while (em->err != 0 && em->err != error) em++;\r
8199       if (em->err != 0) {\r
8200         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8201       } else {\r
8202         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8203       }\r
8204     }\r
8205   }\r
8206   \r
8207   ErrorPopUp(_("Error"), buf);\r
8208 }\r
8209 \r
8210 \r
8211 VOID\r
8212 DisplayMoveError(char *str)\r
8213 {\r
8214   fromX = fromY = -1;\r
8215   ClearHighlights();\r
8216   DrawPosition(FALSE, NULL);\r
8217   if (appData.popupMoveErrors) {\r
8218     ErrorPopUp(_("Error"), str);\r
8219   } else {\r
8220     DisplayMessage(str, "");\r
8221     moveErrorMessageUp = TRUE;\r
8222   }\r
8223 }\r
8224 \r
8225 VOID\r
8226 DisplayFatalError(char *str, int error, int exitStatus)\r
8227 {\r
8228   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8229   int len;\r
8230   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8231 \r
8232   if (error != 0) {\r
8233     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8234                         NULL, error, LANG_NEUTRAL,\r
8235                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8236     if (len > 0) {\r
8237       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8238     } else {\r
8239       ErrorMap *em = errmap;\r
8240       while (em->err != 0 && em->err != error) em++;\r
8241       if (em->err != 0) {\r
8242         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8243       } else {\r
8244         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8245       }\r
8246     }\r
8247     str = buf;\r
8248   }\r
8249   if (appData.debugMode) {\r
8250     fprintf(debugFP, "%s: %s\n", label, str);\r
8251   }\r
8252   if (appData.popupExitMessage) {\r
8253     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8254                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8255   }\r
8256   ExitEvent(exitStatus);\r
8257 }\r
8258 \r
8259 \r
8260 VOID\r
8261 DisplayInformation(char *str)\r
8262 {\r
8263   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8264 }\r
8265 \r
8266 \r
8267 VOID\r
8268 DisplayNote(char *str)\r
8269 {\r
8270   ErrorPopUp(_("Note"), str);\r
8271 }\r
8272 \r
8273 \r
8274 typedef struct {\r
8275   char *title, *question, *replyPrefix;\r
8276   ProcRef pr;\r
8277 } QuestionParams;\r
8278 \r
8279 LRESULT CALLBACK\r
8280 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8281 {\r
8282   static QuestionParams *qp;\r
8283   char reply[MSG_SIZ];\r
8284   int len, err;\r
8285 \r
8286   switch (message) {\r
8287   case WM_INITDIALOG:\r
8288     qp = (QuestionParams *) lParam;\r
8289     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8290     Translate(hDlg, DLG_Question);\r
8291     SetWindowText(hDlg, qp->title);\r
8292     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8293     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8294     return FALSE;\r
8295 \r
8296   case WM_COMMAND:\r
8297     switch (LOWORD(wParam)) {\r
8298     case IDOK:\r
8299       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8300       if (*reply) strcat(reply, " ");\r
8301       len = strlen(reply);\r
8302       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8303       strcat(reply, "\n");\r
8304       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8305       EndDialog(hDlg, TRUE);\r
8306       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8307       return TRUE;\r
8308     case IDCANCEL:\r
8309       EndDialog(hDlg, FALSE);\r
8310       return TRUE;\r
8311     default:\r
8312       break;\r
8313     }\r
8314     break;\r
8315   }\r
8316   return FALSE;\r
8317 }\r
8318 \r
8319 VOID\r
8320 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8321 {\r
8322     QuestionParams qp;\r
8323     FARPROC lpProc;\r
8324     \r
8325     qp.title = title;\r
8326     qp.question = question;\r
8327     qp.replyPrefix = replyPrefix;\r
8328     qp.pr = pr;\r
8329     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8330     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8331       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8332     FreeProcInstance(lpProc);\r
8333 }\r
8334 \r
8335 /* [AS] Pick FRC position */\r
8336 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8337 {\r
8338     static int * lpIndexFRC;\r
8339     BOOL index_is_ok;\r
8340     char buf[16];\r
8341 \r
8342     switch( message )\r
8343     {\r
8344     case WM_INITDIALOG:\r
8345         lpIndexFRC = (int *) lParam;\r
8346 \r
8347         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8348         Translate(hDlg, DLG_NewGameFRC);\r
8349 \r
8350         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8351         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8352         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8353         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8354 \r
8355         break;\r
8356 \r
8357     case WM_COMMAND:\r
8358         switch( LOWORD(wParam) ) {\r
8359         case IDOK:\r
8360             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8361             EndDialog( hDlg, 0 );\r
8362             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8363             return TRUE;\r
8364         case IDCANCEL:\r
8365             EndDialog( hDlg, 1 );   \r
8366             return TRUE;\r
8367         case IDC_NFG_Edit:\r
8368             if( HIWORD(wParam) == EN_CHANGE ) {\r
8369                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8370 \r
8371                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8372             }\r
8373             return TRUE;\r
8374         case IDC_NFG_Random:\r
8375           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8376             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8377             return TRUE;\r
8378         }\r
8379 \r
8380         break;\r
8381     }\r
8382 \r
8383     return FALSE;\r
8384 }\r
8385 \r
8386 int NewGameFRC()\r
8387 {\r
8388     int result;\r
8389     int index = appData.defaultFrcPosition;\r
8390     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8391 \r
8392     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8393 \r
8394     if( result == 0 ) {\r
8395         appData.defaultFrcPosition = index;\r
8396     }\r
8397 \r
8398     return result;\r
8399 }\r
8400 \r
8401 /* [AS] Game list options. Refactored by HGM */\r
8402 \r
8403 HWND gameListOptionsDialog;\r
8404 \r
8405 // low-level front-end: clear text edit / list widget\r
8406 void\r
8407 GLT_ClearList()\r
8408 {\r
8409     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8410 }\r
8411 \r
8412 // low-level front-end: clear text edit / list widget\r
8413 void\r
8414 GLT_DeSelectList()\r
8415 {\r
8416     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8417 }\r
8418 \r
8419 // low-level front-end: append line to text edit / list widget\r
8420 void\r
8421 GLT_AddToList( char *name )\r
8422 {\r
8423     if( name != 0 ) {\r
8424             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8425     }\r
8426 }\r
8427 \r
8428 // low-level front-end: get line from text edit / list widget\r
8429 Boolean\r
8430 GLT_GetFromList( int index, char *name )\r
8431 {\r
8432     if( name != 0 ) {\r
8433             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8434                 return TRUE;\r
8435     }\r
8436     return FALSE;\r
8437 }\r
8438 \r
8439 void GLT_MoveSelection( HWND hDlg, int delta )\r
8440 {\r
8441     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8442     int idx2 = idx1 + delta;\r
8443     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8444 \r
8445     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8446         char buf[128];\r
8447 \r
8448         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8449         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8450         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8451         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8452     }\r
8453 }\r
8454 \r
8455 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8456 {\r
8457     switch( message )\r
8458     {\r
8459     case WM_INITDIALOG:\r
8460         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8461         \r
8462         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8463         Translate(hDlg, DLG_GameListOptions);\r
8464 \r
8465         /* Initialize list */\r
8466         GLT_TagsToList( lpUserGLT );\r
8467 \r
8468         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8469 \r
8470         break;\r
8471 \r
8472     case WM_COMMAND:\r
8473         switch( LOWORD(wParam) ) {\r
8474         case IDOK:\r
8475             GLT_ParseList();\r
8476             EndDialog( hDlg, 0 );\r
8477             return TRUE;\r
8478         case IDCANCEL:\r
8479             EndDialog( hDlg, 1 );\r
8480             return TRUE;\r
8481 \r
8482         case IDC_GLT_Default:\r
8483             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8484             return TRUE;\r
8485 \r
8486         case IDC_GLT_Restore:\r
8487             GLT_TagsToList( appData.gameListTags );\r
8488             return TRUE;\r
8489 \r
8490         case IDC_GLT_Up:\r
8491             GLT_MoveSelection( hDlg, -1 );\r
8492             return TRUE;\r
8493 \r
8494         case IDC_GLT_Down:\r
8495             GLT_MoveSelection( hDlg, +1 );\r
8496             return TRUE;\r
8497         }\r
8498 \r
8499         break;\r
8500     }\r
8501 \r
8502     return FALSE;\r
8503 }\r
8504 \r
8505 int GameListOptions()\r
8506 {\r
8507     int result;\r
8508     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8509 \r
8510       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8511 \r
8512     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8513 \r
8514     if( result == 0 ) {\r
8515         /* [AS] Memory leak here! */\r
8516         appData.gameListTags = strdup( lpUserGLT ); \r
8517     }\r
8518 \r
8519     return result;\r
8520 }\r
8521 \r
8522 VOID\r
8523 DisplayIcsInteractionTitle(char *str)\r
8524 {\r
8525   char consoleTitle[MSG_SIZ];\r
8526 \r
8527     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8528   SetWindowText(hwndConsole, consoleTitle);\r
8529 }\r
8530 \r
8531 void\r
8532 DrawPosition(int fullRedraw, Board board)\r
8533 {\r
8534   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8535 }\r
8536 \r
8537 void NotifyFrontendLogin()\r
8538 {\r
8539         if (hwndConsole)\r
8540                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8541 }\r
8542 \r
8543 VOID\r
8544 ResetFrontEnd()\r
8545 {\r
8546   fromX = fromY = -1;\r
8547   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8548     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8549     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8550     dragInfo.lastpos = dragInfo.pos;\r
8551     dragInfo.start.x = dragInfo.start.y = -1;\r
8552     dragInfo.from = dragInfo.start;\r
8553     ReleaseCapture();\r
8554     DrawPosition(TRUE, NULL);\r
8555   }\r
8556   TagsPopDown();\r
8557 }\r
8558 \r
8559 \r
8560 VOID\r
8561 CommentPopUp(char *title, char *str)\r
8562 {\r
8563   HWND hwnd = GetActiveWindow();\r
8564   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8565   SAY(str);\r
8566   SetActiveWindow(hwnd);\r
8567 }\r
8568 \r
8569 VOID\r
8570 CommentPopDown(void)\r
8571 {\r
8572   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8573   if (commentDialog) {\r
8574     ShowWindow(commentDialog, SW_HIDE);\r
8575   }\r
8576   commentUp = FALSE;\r
8577 }\r
8578 \r
8579 VOID\r
8580 EditCommentPopUp(int index, char *title, char *str)\r
8581 {\r
8582   EitherCommentPopUp(index, title, str, TRUE);\r
8583 }\r
8584 \r
8585 \r
8586 VOID\r
8587 RingBell()\r
8588 {\r
8589   MyPlaySound(&sounds[(int)SoundMove]);\r
8590 }\r
8591 \r
8592 VOID PlayIcsWinSound()\r
8593 {\r
8594   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8595 }\r
8596 \r
8597 VOID PlayIcsLossSound()\r
8598 {\r
8599   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8600 }\r
8601 \r
8602 VOID PlayIcsDrawSound()\r
8603 {\r
8604   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8605 }\r
8606 \r
8607 VOID PlayIcsUnfinishedSound()\r
8608 {\r
8609   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8610 }\r
8611 \r
8612 VOID\r
8613 PlayAlarmSound()\r
8614 {\r
8615   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8616 }\r
8617 \r
8618 VOID\r
8619 PlayTellSound()\r
8620 {\r
8621   MyPlaySound(&textAttribs[ColorTell].sound);\r
8622 }\r
8623 \r
8624 \r
8625 VOID\r
8626 EchoOn()\r
8627 {\r
8628   HWND hInput;\r
8629   consoleEcho = TRUE;\r
8630   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8631   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8632   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8633 }\r
8634 \r
8635 \r
8636 VOID\r
8637 EchoOff()\r
8638 {\r
8639   CHARFORMAT cf;\r
8640   HWND hInput;\r
8641   consoleEcho = FALSE;\r
8642   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8643   /* This works OK: set text and background both to the same color */\r
8644   cf = consoleCF;\r
8645   cf.crTextColor = COLOR_ECHOOFF;\r
8646   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8647   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8648 }\r
8649 \r
8650 /* No Raw()...? */\r
8651 \r
8652 void Colorize(ColorClass cc, int continuation)\r
8653 {\r
8654   currentColorClass = cc;\r
8655   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8656   consoleCF.crTextColor = textAttribs[cc].color;\r
8657   consoleCF.dwEffects = textAttribs[cc].effects;\r
8658   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8659 }\r
8660 \r
8661 char *\r
8662 UserName()\r
8663 {\r
8664   static char buf[MSG_SIZ];\r
8665   DWORD bufsiz = MSG_SIZ;\r
8666 \r
8667   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8668         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8669   }\r
8670   if (!GetUserName(buf, &bufsiz)) {\r
8671     /*DisplayError("Error getting user name", GetLastError());*/\r
8672     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8673   }\r
8674   return buf;\r
8675 }\r
8676 \r
8677 char *\r
8678 HostName()\r
8679 {\r
8680   static char buf[MSG_SIZ];\r
8681   DWORD bufsiz = MSG_SIZ;\r
8682 \r
8683   if (!GetComputerName(buf, &bufsiz)) {\r
8684     /*DisplayError("Error getting host name", GetLastError());*/\r
8685     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8686   }\r
8687   return buf;\r
8688 }\r
8689 \r
8690 \r
8691 int\r
8692 ClockTimerRunning()\r
8693 {\r
8694   return clockTimerEvent != 0;\r
8695 }\r
8696 \r
8697 int\r
8698 StopClockTimer()\r
8699 {\r
8700   if (clockTimerEvent == 0) return FALSE;\r
8701   KillTimer(hwndMain, clockTimerEvent);\r
8702   clockTimerEvent = 0;\r
8703   return TRUE;\r
8704 }\r
8705 \r
8706 void\r
8707 StartClockTimer(long millisec)\r
8708 {\r
8709   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8710                              (UINT) millisec, NULL);\r
8711 }\r
8712 \r
8713 void\r
8714 DisplayWhiteClock(long timeRemaining, int highlight)\r
8715 {\r
8716   HDC hdc;\r
8717   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8718 \r
8719   if(appData.noGUI) return;\r
8720   hdc = GetDC(hwndMain);\r
8721   if (!IsIconic(hwndMain)) {\r
8722     DisplayAClock(hdc, timeRemaining, highlight, \r
8723                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8724   }\r
8725   if (highlight && iconCurrent == iconBlack) {\r
8726     iconCurrent = iconWhite;\r
8727     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8728     if (IsIconic(hwndMain)) {\r
8729       DrawIcon(hdc, 2, 2, iconCurrent);\r
8730     }\r
8731   }\r
8732   (void) ReleaseDC(hwndMain, hdc);\r
8733   if (hwndConsole)\r
8734     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8735 }\r
8736 \r
8737 void\r
8738 DisplayBlackClock(long timeRemaining, int highlight)\r
8739 {\r
8740   HDC hdc;\r
8741   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8742 \r
8743   if(appData.noGUI) return;\r
8744   hdc = GetDC(hwndMain);\r
8745   if (!IsIconic(hwndMain)) {\r
8746     DisplayAClock(hdc, timeRemaining, highlight, \r
8747                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8748   }\r
8749   if (highlight && iconCurrent == iconWhite) {\r
8750     iconCurrent = iconBlack;\r
8751     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8752     if (IsIconic(hwndMain)) {\r
8753       DrawIcon(hdc, 2, 2, iconCurrent);\r
8754     }\r
8755   }\r
8756   (void) ReleaseDC(hwndMain, hdc);\r
8757   if (hwndConsole)\r
8758     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8759 }\r
8760 \r
8761 \r
8762 int\r
8763 LoadGameTimerRunning()\r
8764 {\r
8765   return loadGameTimerEvent != 0;\r
8766 }\r
8767 \r
8768 int\r
8769 StopLoadGameTimer()\r
8770 {\r
8771   if (loadGameTimerEvent == 0) return FALSE;\r
8772   KillTimer(hwndMain, loadGameTimerEvent);\r
8773   loadGameTimerEvent = 0;\r
8774   return TRUE;\r
8775 }\r
8776 \r
8777 void\r
8778 StartLoadGameTimer(long millisec)\r
8779 {\r
8780   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8781                                 (UINT) millisec, NULL);\r
8782 }\r
8783 \r
8784 void\r
8785 AutoSaveGame()\r
8786 {\r
8787   char *defName;\r
8788   FILE *f;\r
8789   char fileTitle[MSG_SIZ];\r
8790 \r
8791   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8792   f = OpenFileDialog(hwndMain, "a", defName,\r
8793                      appData.oldSaveStyle ? "gam" : "pgn",\r
8794                      GAME_FILT, \r
8795                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8796   if (f != NULL) {\r
8797     SaveGame(f, 0, "");\r
8798     fclose(f);\r
8799   }\r
8800 }\r
8801 \r
8802 \r
8803 void\r
8804 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8805 {\r
8806   if (delayedTimerEvent != 0) {\r
8807     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8808       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8809     }\r
8810     KillTimer(hwndMain, delayedTimerEvent);\r
8811     delayedTimerEvent = 0;\r
8812     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8813     delayedTimerCallback();\r
8814   }\r
8815   delayedTimerCallback = cb;\r
8816   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8817                                 (UINT) millisec, NULL);\r
8818 }\r
8819 \r
8820 DelayedEventCallback\r
8821 GetDelayedEvent()\r
8822 {\r
8823   if (delayedTimerEvent) {\r
8824     return delayedTimerCallback;\r
8825   } else {\r
8826     return NULL;\r
8827   }\r
8828 }\r
8829 \r
8830 void\r
8831 CancelDelayedEvent()\r
8832 {\r
8833   if (delayedTimerEvent) {\r
8834     KillTimer(hwndMain, delayedTimerEvent);\r
8835     delayedTimerEvent = 0;\r
8836   }\r
8837 }\r
8838 \r
8839 DWORD GetWin32Priority(int nice)\r
8840 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8841 /*\r
8842 REALTIME_PRIORITY_CLASS     0x00000100\r
8843 HIGH_PRIORITY_CLASS         0x00000080\r
8844 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8845 NORMAL_PRIORITY_CLASS       0x00000020\r
8846 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8847 IDLE_PRIORITY_CLASS         0x00000040\r
8848 */\r
8849         if (nice < -15) return 0x00000080;\r
8850         if (nice < 0)   return 0x00008000;\r
8851         if (nice == 0)  return 0x00000020;\r
8852         if (nice < 15)  return 0x00004000;\r
8853         return 0x00000040;\r
8854 }\r
8855 \r
8856 void RunCommand(char *cmdLine)\r
8857 {\r
8858   /* Now create the child process. */\r
8859   STARTUPINFO siStartInfo;\r
8860   PROCESS_INFORMATION piProcInfo;\r
8861 \r
8862   siStartInfo.cb = sizeof(STARTUPINFO);\r
8863   siStartInfo.lpReserved = NULL;\r
8864   siStartInfo.lpDesktop = NULL;\r
8865   siStartInfo.lpTitle = NULL;\r
8866   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8867   siStartInfo.cbReserved2 = 0;\r
8868   siStartInfo.lpReserved2 = NULL;\r
8869   siStartInfo.hStdInput = NULL;\r
8870   siStartInfo.hStdOutput = NULL;\r
8871   siStartInfo.hStdError = NULL;\r
8872 \r
8873   CreateProcess(NULL,\r
8874                 cmdLine,           /* command line */\r
8875                 NULL,      /* process security attributes */\r
8876                 NULL,      /* primary thread security attrs */\r
8877                 TRUE,      /* handles are inherited */\r
8878                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8879                 NULL,      /* use parent's environment */\r
8880                 NULL,\r
8881                 &siStartInfo, /* STARTUPINFO pointer */\r
8882                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8883 \r
8884   CloseHandle(piProcInfo.hThread);\r
8885 }\r
8886 \r
8887 /* Start a child process running the given program.\r
8888    The process's standard output can be read from "from", and its\r
8889    standard input can be written to "to".\r
8890    Exit with fatal error if anything goes wrong.\r
8891    Returns an opaque pointer that can be used to destroy the process\r
8892    later.\r
8893 */\r
8894 int\r
8895 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8896 {\r
8897 #define BUFSIZE 4096\r
8898 \r
8899   HANDLE hChildStdinRd, hChildStdinWr,\r
8900     hChildStdoutRd, hChildStdoutWr;\r
8901   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8902   SECURITY_ATTRIBUTES saAttr;\r
8903   BOOL fSuccess;\r
8904   PROCESS_INFORMATION piProcInfo;\r
8905   STARTUPINFO siStartInfo;\r
8906   ChildProc *cp;\r
8907   char buf[MSG_SIZ];\r
8908   DWORD err;\r
8909 \r
8910   if (appData.debugMode) {\r
8911     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8912   }\r
8913 \r
8914   *pr = NoProc;\r
8915 \r
8916   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8917   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8918   saAttr.bInheritHandle = TRUE;\r
8919   saAttr.lpSecurityDescriptor = NULL;\r
8920 \r
8921   /*\r
8922    * The steps for redirecting child's STDOUT:\r
8923    *     1. Create anonymous pipe to be STDOUT for child.\r
8924    *     2. Create a noninheritable duplicate of read handle,\r
8925    *         and close the inheritable read handle.\r
8926    */\r
8927 \r
8928   /* Create a pipe for the child's STDOUT. */\r
8929   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8930     return GetLastError();\r
8931   }\r
8932 \r
8933   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8934   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8935                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8936                              FALSE,     /* not inherited */\r
8937                              DUPLICATE_SAME_ACCESS);\r
8938   if (! fSuccess) {\r
8939     return GetLastError();\r
8940   }\r
8941   CloseHandle(hChildStdoutRd);\r
8942 \r
8943   /*\r
8944    * The steps for redirecting child's STDIN:\r
8945    *     1. Create anonymous pipe to be STDIN for child.\r
8946    *     2. Create a noninheritable duplicate of write handle,\r
8947    *         and close the inheritable write handle.\r
8948    */\r
8949 \r
8950   /* Create a pipe for the child's STDIN. */\r
8951   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8952     return GetLastError();\r
8953   }\r
8954 \r
8955   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8956   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8957                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8958                              FALSE,     /* not inherited */\r
8959                              DUPLICATE_SAME_ACCESS);\r
8960   if (! fSuccess) {\r
8961     return GetLastError();\r
8962   }\r
8963   CloseHandle(hChildStdinWr);\r
8964 \r
8965   /* Arrange to (1) look in dir for the child .exe file, and\r
8966    * (2) have dir be the child's working directory.  Interpret\r
8967    * dir relative to the directory WinBoard loaded from. */\r
8968   GetCurrentDirectory(MSG_SIZ, buf);\r
8969   SetCurrentDirectory(installDir);\r
8970   SetCurrentDirectory(dir);\r
8971 \r
8972   /* Now create the child process. */\r
8973 \r
8974   siStartInfo.cb = sizeof(STARTUPINFO);\r
8975   siStartInfo.lpReserved = NULL;\r
8976   siStartInfo.lpDesktop = NULL;\r
8977   siStartInfo.lpTitle = NULL;\r
8978   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8979   siStartInfo.cbReserved2 = 0;\r
8980   siStartInfo.lpReserved2 = NULL;\r
8981   siStartInfo.hStdInput = hChildStdinRd;\r
8982   siStartInfo.hStdOutput = hChildStdoutWr;\r
8983   siStartInfo.hStdError = hChildStdoutWr;\r
8984 \r
8985   fSuccess = CreateProcess(NULL,\r
8986                            cmdLine,        /* command line */\r
8987                            NULL,           /* process security attributes */\r
8988                            NULL,           /* primary thread security attrs */\r
8989                            TRUE,           /* handles are inherited */\r
8990                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8991                            NULL,           /* use parent's environment */\r
8992                            NULL,\r
8993                            &siStartInfo, /* STARTUPINFO pointer */\r
8994                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8995 \r
8996   err = GetLastError();\r
8997   SetCurrentDirectory(buf); /* return to prev directory */\r
8998   if (! fSuccess) {\r
8999     return err;\r
9000   }\r
9001 \r
9002   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9003     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9004     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9005   }\r
9006 \r
9007   /* Close the handles we don't need in the parent */\r
9008   CloseHandle(piProcInfo.hThread);\r
9009   CloseHandle(hChildStdinRd);\r
9010   CloseHandle(hChildStdoutWr);\r
9011 \r
9012   /* Prepare return value */\r
9013   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9014   cp->kind = CPReal;\r
9015   cp->hProcess = piProcInfo.hProcess;\r
9016   cp->pid = piProcInfo.dwProcessId;\r
9017   cp->hFrom = hChildStdoutRdDup;\r
9018   cp->hTo = hChildStdinWrDup;\r
9019 \r
9020   *pr = (void *) cp;\r
9021 \r
9022   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9023      2000 where engines sometimes don't see the initial command(s)\r
9024      from WinBoard and hang.  I don't understand how that can happen,\r
9025      but the Sleep is harmless, so I've put it in.  Others have also\r
9026      reported what may be the same problem, so hopefully this will fix\r
9027      it for them too.  */\r
9028   Sleep(500);\r
9029 \r
9030   return NO_ERROR;\r
9031 }\r
9032 \r
9033 \r
9034 void\r
9035 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9036 {\r
9037   ChildProc *cp; int result;\r
9038 \r
9039   cp = (ChildProc *) pr;\r
9040   if (cp == NULL) return;\r
9041 \r
9042   switch (cp->kind) {\r
9043   case CPReal:\r
9044     /* TerminateProcess is considered harmful, so... */\r
9045     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9046     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9047     /* The following doesn't work because the chess program\r
9048        doesn't "have the same console" as WinBoard.  Maybe\r
9049        we could arrange for this even though neither WinBoard\r
9050        nor the chess program uses a console for stdio? */\r
9051     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9052 \r
9053     /* [AS] Special termination modes for misbehaving programs... */\r
9054     if( signal == 9 ) { \r
9055         result = TerminateProcess( cp->hProcess, 0 );\r
9056 \r
9057         if ( appData.debugMode) {\r
9058             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9059         }\r
9060     }\r
9061     else if( signal == 10 ) {\r
9062         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9063 \r
9064         if( dw != WAIT_OBJECT_0 ) {\r
9065             result = TerminateProcess( cp->hProcess, 0 );\r
9066 \r
9067             if ( appData.debugMode) {\r
9068                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9069             }\r
9070 \r
9071         }\r
9072     }\r
9073 \r
9074     CloseHandle(cp->hProcess);\r
9075     break;\r
9076 \r
9077   case CPComm:\r
9078     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9079     break;\r
9080 \r
9081   case CPSock:\r
9082     closesocket(cp->sock);\r
9083     WSACleanup();\r
9084     break;\r
9085 \r
9086   case CPRcmd:\r
9087     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9088     closesocket(cp->sock);\r
9089     closesocket(cp->sock2);\r
9090     WSACleanup();\r
9091     break;\r
9092   }\r
9093   free(cp);\r
9094 }\r
9095 \r
9096 void\r
9097 InterruptChildProcess(ProcRef pr)\r
9098 {\r
9099   ChildProc *cp;\r
9100 \r
9101   cp = (ChildProc *) pr;\r
9102   if (cp == NULL) return;\r
9103   switch (cp->kind) {\r
9104   case CPReal:\r
9105     /* The following doesn't work because the chess program\r
9106        doesn't "have the same console" as WinBoard.  Maybe\r
9107        we could arrange for this even though neither WinBoard\r
9108        nor the chess program uses a console for stdio */\r
9109     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9110     break;\r
9111 \r
9112   case CPComm:\r
9113   case CPSock:\r
9114     /* Can't interrupt */\r
9115     break;\r
9116 \r
9117   case CPRcmd:\r
9118     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9119     break;\r
9120   }\r
9121 }\r
9122 \r
9123 \r
9124 int\r
9125 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9126 {\r
9127   char cmdLine[MSG_SIZ];\r
9128 \r
9129   if (port[0] == NULLCHAR) {\r
9130     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9131   } else {\r
9132     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9133   }\r
9134   return StartChildProcess(cmdLine, "", pr);\r
9135 }\r
9136 \r
9137 \r
9138 /* Code to open TCP sockets */\r
9139 \r
9140 int\r
9141 OpenTCP(char *host, char *port, ProcRef *pr)\r
9142 {\r
9143   ChildProc *cp;\r
9144   int err;\r
9145   SOCKET s;\r
9146   struct sockaddr_in sa, mysa;\r
9147   struct hostent FAR *hp;\r
9148   unsigned short uport;\r
9149   WORD wVersionRequested;\r
9150   WSADATA wsaData;\r
9151 \r
9152   /* Initialize socket DLL */\r
9153   wVersionRequested = MAKEWORD(1, 1);\r
9154   err = WSAStartup(wVersionRequested, &wsaData);\r
9155   if (err != 0) return err;\r
9156 \r
9157   /* Make socket */\r
9158   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9159     err = WSAGetLastError();\r
9160     WSACleanup();\r
9161     return err;\r
9162   }\r
9163 \r
9164   /* Bind local address using (mostly) don't-care values.\r
9165    */\r
9166   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9167   mysa.sin_family = AF_INET;\r
9168   mysa.sin_addr.s_addr = INADDR_ANY;\r
9169   uport = (unsigned short) 0;\r
9170   mysa.sin_port = htons(uport);\r
9171   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9172       == SOCKET_ERROR) {\r
9173     err = WSAGetLastError();\r
9174     WSACleanup();\r
9175     return err;\r
9176   }\r
9177 \r
9178   /* Resolve remote host name */\r
9179   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9180   if (!(hp = gethostbyname(host))) {\r
9181     unsigned int b0, b1, b2, b3;\r
9182 \r
9183     err = WSAGetLastError();\r
9184 \r
9185     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9186       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9187       hp->h_addrtype = AF_INET;\r
9188       hp->h_length = 4;\r
9189       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9190       hp->h_addr_list[0] = (char *) malloc(4);\r
9191       hp->h_addr_list[0][0] = (char) b0;\r
9192       hp->h_addr_list[0][1] = (char) b1;\r
9193       hp->h_addr_list[0][2] = (char) b2;\r
9194       hp->h_addr_list[0][3] = (char) b3;\r
9195     } else {\r
9196       WSACleanup();\r
9197       return err;\r
9198     }\r
9199   }\r
9200   sa.sin_family = hp->h_addrtype;\r
9201   uport = (unsigned short) atoi(port);\r
9202   sa.sin_port = htons(uport);\r
9203   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9204 \r
9205   /* Make connection */\r
9206   if (connect(s, (struct sockaddr *) &sa,\r
9207               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9208     err = WSAGetLastError();\r
9209     WSACleanup();\r
9210     return err;\r
9211   }\r
9212 \r
9213   /* Prepare return value */\r
9214   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9215   cp->kind = CPSock;\r
9216   cp->sock = s;\r
9217   *pr = (ProcRef *) cp;\r
9218 \r
9219   return NO_ERROR;\r
9220 }\r
9221 \r
9222 int\r
9223 OpenCommPort(char *name, ProcRef *pr)\r
9224 {\r
9225   HANDLE h;\r
9226   COMMTIMEOUTS ct;\r
9227   ChildProc *cp;\r
9228   char fullname[MSG_SIZ];\r
9229 \r
9230   if (*name != '\\')\r
9231     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9232   else\r
9233     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9234 \r
9235   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9236                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9237   if (h == (HANDLE) -1) {\r
9238     return GetLastError();\r
9239   }\r
9240   hCommPort = h;\r
9241 \r
9242   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9243 \r
9244   /* Accumulate characters until a 100ms pause, then parse */\r
9245   ct.ReadIntervalTimeout = 100;\r
9246   ct.ReadTotalTimeoutMultiplier = 0;\r
9247   ct.ReadTotalTimeoutConstant = 0;\r
9248   ct.WriteTotalTimeoutMultiplier = 0;\r
9249   ct.WriteTotalTimeoutConstant = 0;\r
9250   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9251 \r
9252   /* Prepare return value */\r
9253   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9254   cp->kind = CPComm;\r
9255   cp->hFrom = h;\r
9256   cp->hTo = h;\r
9257   *pr = (ProcRef *) cp;\r
9258 \r
9259   return NO_ERROR;\r
9260 }\r
9261 \r
9262 int\r
9263 OpenLoopback(ProcRef *pr)\r
9264 {\r
9265   DisplayFatalError(_("Not implemented"), 0, 1);\r
9266   return NO_ERROR;\r
9267 }\r
9268 \r
9269 \r
9270 int\r
9271 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9272 {\r
9273   ChildProc *cp;\r
9274   int err;\r
9275   SOCKET s, s2, s3;\r
9276   struct sockaddr_in sa, mysa;\r
9277   struct hostent FAR *hp;\r
9278   unsigned short uport;\r
9279   WORD wVersionRequested;\r
9280   WSADATA wsaData;\r
9281   int fromPort;\r
9282   char stderrPortStr[MSG_SIZ];\r
9283 \r
9284   /* Initialize socket DLL */\r
9285   wVersionRequested = MAKEWORD(1, 1);\r
9286   err = WSAStartup(wVersionRequested, &wsaData);\r
9287   if (err != 0) return err;\r
9288 \r
9289   /* Resolve remote host name */\r
9290   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9291   if (!(hp = gethostbyname(host))) {\r
9292     unsigned int b0, b1, b2, b3;\r
9293 \r
9294     err = WSAGetLastError();\r
9295 \r
9296     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9297       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9298       hp->h_addrtype = AF_INET;\r
9299       hp->h_length = 4;\r
9300       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9301       hp->h_addr_list[0] = (char *) malloc(4);\r
9302       hp->h_addr_list[0][0] = (char) b0;\r
9303       hp->h_addr_list[0][1] = (char) b1;\r
9304       hp->h_addr_list[0][2] = (char) b2;\r
9305       hp->h_addr_list[0][3] = (char) b3;\r
9306     } else {\r
9307       WSACleanup();\r
9308       return err;\r
9309     }\r
9310   }\r
9311   sa.sin_family = hp->h_addrtype;\r
9312   uport = (unsigned short) 514;\r
9313   sa.sin_port = htons(uport);\r
9314   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9315 \r
9316   /* Bind local socket to unused "privileged" port address\r
9317    */\r
9318   s = INVALID_SOCKET;\r
9319   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9320   mysa.sin_family = AF_INET;\r
9321   mysa.sin_addr.s_addr = INADDR_ANY;\r
9322   for (fromPort = 1023;; fromPort--) {\r
9323     if (fromPort < 0) {\r
9324       WSACleanup();\r
9325       return WSAEADDRINUSE;\r
9326     }\r
9327     if (s == INVALID_SOCKET) {\r
9328       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9329         err = WSAGetLastError();\r
9330         WSACleanup();\r
9331         return err;\r
9332       }\r
9333     }\r
9334     uport = (unsigned short) fromPort;\r
9335     mysa.sin_port = htons(uport);\r
9336     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9337         == SOCKET_ERROR) {\r
9338       err = WSAGetLastError();\r
9339       if (err == WSAEADDRINUSE) continue;\r
9340       WSACleanup();\r
9341       return err;\r
9342     }\r
9343     if (connect(s, (struct sockaddr *) &sa,\r
9344       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9345       err = WSAGetLastError();\r
9346       if (err == WSAEADDRINUSE) {\r
9347         closesocket(s);\r
9348         s = -1;\r
9349         continue;\r
9350       }\r
9351       WSACleanup();\r
9352       return err;\r
9353     }\r
9354     break;\r
9355   }\r
9356 \r
9357   /* Bind stderr local socket to unused "privileged" port address\r
9358    */\r
9359   s2 = INVALID_SOCKET;\r
9360   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9361   mysa.sin_family = AF_INET;\r
9362   mysa.sin_addr.s_addr = INADDR_ANY;\r
9363   for (fromPort = 1023;; fromPort--) {\r
9364     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9365     if (fromPort < 0) {\r
9366       (void) closesocket(s);\r
9367       WSACleanup();\r
9368       return WSAEADDRINUSE;\r
9369     }\r
9370     if (s2 == INVALID_SOCKET) {\r
9371       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9372         err = WSAGetLastError();\r
9373         closesocket(s);\r
9374         WSACleanup();\r
9375         return err;\r
9376       }\r
9377     }\r
9378     uport = (unsigned short) fromPort;\r
9379     mysa.sin_port = htons(uport);\r
9380     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9381         == SOCKET_ERROR) {\r
9382       err = WSAGetLastError();\r
9383       if (err == WSAEADDRINUSE) continue;\r
9384       (void) closesocket(s);\r
9385       WSACleanup();\r
9386       return err;\r
9387     }\r
9388     if (listen(s2, 1) == SOCKET_ERROR) {\r
9389       err = WSAGetLastError();\r
9390       if (err == WSAEADDRINUSE) {\r
9391         closesocket(s2);\r
9392         s2 = INVALID_SOCKET;\r
9393         continue;\r
9394       }\r
9395       (void) closesocket(s);\r
9396       (void) closesocket(s2);\r
9397       WSACleanup();\r
9398       return err;\r
9399     }\r
9400     break;\r
9401   }\r
9402   prevStderrPort = fromPort; // remember port used\r
9403   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9404 \r
9405   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9406     err = WSAGetLastError();\r
9407     (void) closesocket(s);\r
9408     (void) closesocket(s2);\r
9409     WSACleanup();\r
9410     return err;\r
9411   }\r
9412 \r
9413   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9414     err = WSAGetLastError();\r
9415     (void) closesocket(s);\r
9416     (void) closesocket(s2);\r
9417     WSACleanup();\r
9418     return err;\r
9419   }\r
9420   if (*user == NULLCHAR) user = UserName();\r
9421   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9422     err = WSAGetLastError();\r
9423     (void) closesocket(s);\r
9424     (void) closesocket(s2);\r
9425     WSACleanup();\r
9426     return err;\r
9427   }\r
9428   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9429     err = WSAGetLastError();\r
9430     (void) closesocket(s);\r
9431     (void) closesocket(s2);\r
9432     WSACleanup();\r
9433     return err;\r
9434   }\r
9435 \r
9436   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9437     err = WSAGetLastError();\r
9438     (void) closesocket(s);\r
9439     (void) closesocket(s2);\r
9440     WSACleanup();\r
9441     return err;\r
9442   }\r
9443   (void) closesocket(s2);  /* Stop listening */\r
9444 \r
9445   /* Prepare return value */\r
9446   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9447   cp->kind = CPRcmd;\r
9448   cp->sock = s;\r
9449   cp->sock2 = s3;\r
9450   *pr = (ProcRef *) cp;\r
9451 \r
9452   return NO_ERROR;\r
9453 }\r
9454 \r
9455 \r
9456 InputSourceRef\r
9457 AddInputSource(ProcRef pr, int lineByLine,\r
9458                InputCallback func, VOIDSTAR closure)\r
9459 {\r
9460   InputSource *is, *is2 = NULL;\r
9461   ChildProc *cp = (ChildProc *) pr;\r
9462 \r
9463   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9464   is->lineByLine = lineByLine;\r
9465   is->func = func;\r
9466   is->closure = closure;\r
9467   is->second = NULL;\r
9468   is->next = is->buf;\r
9469   if (pr == NoProc) {\r
9470     is->kind = CPReal;\r
9471     consoleInputSource = is;\r
9472   } else {\r
9473     is->kind = cp->kind;\r
9474     /* \r
9475         [AS] Try to avoid a race condition if the thread is given control too early:\r
9476         we create all threads suspended so that the is->hThread variable can be\r
9477         safely assigned, then let the threads start with ResumeThread.\r
9478     */\r
9479     switch (cp->kind) {\r
9480     case CPReal:\r
9481       is->hFile = cp->hFrom;\r
9482       cp->hFrom = NULL; /* now owned by InputThread */\r
9483       is->hThread =\r
9484         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9485                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9486       break;\r
9487 \r
9488     case CPComm:\r
9489       is->hFile = cp->hFrom;\r
9490       cp->hFrom = NULL; /* now owned by InputThread */\r
9491       is->hThread =\r
9492         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9493                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9494       break;\r
9495 \r
9496     case CPSock:\r
9497       is->sock = cp->sock;\r
9498       is->hThread =\r
9499         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9500                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9501       break;\r
9502 \r
9503     case CPRcmd:\r
9504       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9505       *is2 = *is;\r
9506       is->sock = cp->sock;\r
9507       is->second = is2;\r
9508       is2->sock = cp->sock2;\r
9509       is2->second = is2;\r
9510       is->hThread =\r
9511         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9512                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9513       is2->hThread =\r
9514         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9515                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9516       break;\r
9517     }\r
9518 \r
9519     if( is->hThread != NULL ) {\r
9520         ResumeThread( is->hThread );\r
9521     }\r
9522 \r
9523     if( is2 != NULL && is2->hThread != NULL ) {\r
9524         ResumeThread( is2->hThread );\r
9525     }\r
9526   }\r
9527 \r
9528   return (InputSourceRef) is;\r
9529 }\r
9530 \r
9531 void\r
9532 RemoveInputSource(InputSourceRef isr)\r
9533 {\r
9534   InputSource *is;\r
9535 \r
9536   is = (InputSource *) isr;\r
9537   is->hThread = NULL;  /* tell thread to stop */\r
9538   CloseHandle(is->hThread);\r
9539   if (is->second != NULL) {\r
9540     is->second->hThread = NULL;\r
9541     CloseHandle(is->second->hThread);\r
9542   }\r
9543 }\r
9544 \r
9545 int no_wrap(char *message, int count)\r
9546 {\r
9547     ConsoleOutput(message, count, FALSE);\r
9548     return count;\r
9549 }\r
9550 \r
9551 int\r
9552 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9553 {\r
9554   DWORD dOutCount;\r
9555   int outCount = SOCKET_ERROR;\r
9556   ChildProc *cp = (ChildProc *) pr;\r
9557   static OVERLAPPED ovl;\r
9558   static int line = 0;\r
9559 \r
9560   if (pr == NoProc)\r
9561   {\r
9562     if (appData.noJoin || !appData.useInternalWrap)\r
9563       return no_wrap(message, count);\r
9564     else\r
9565     {\r
9566       int width = get_term_width();\r
9567       int len = wrap(NULL, message, count, width, &line);\r
9568       char *msg = malloc(len);\r
9569       int dbgchk;\r
9570 \r
9571       if (!msg)\r
9572         return no_wrap(message, count);\r
9573       else\r
9574       {\r
9575         dbgchk = wrap(msg, message, count, width, &line);\r
9576         if (dbgchk != len && appData.debugMode)\r
9577             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9578         ConsoleOutput(msg, len, FALSE);\r
9579         free(msg);\r
9580         return len;\r
9581       }\r
9582     }\r
9583   }\r
9584 \r
9585   if (ovl.hEvent == NULL) {\r
9586     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9587   }\r
9588   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9589 \r
9590   switch (cp->kind) {\r
9591   case CPSock:\r
9592   case CPRcmd:\r
9593     outCount = send(cp->sock, message, count, 0);\r
9594     if (outCount == SOCKET_ERROR) {\r
9595       *outError = WSAGetLastError();\r
9596     } else {\r
9597       *outError = NO_ERROR;\r
9598     }\r
9599     break;\r
9600 \r
9601   case CPReal:\r
9602     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9603                   &dOutCount, NULL)) {\r
9604       *outError = NO_ERROR;\r
9605       outCount = (int) dOutCount;\r
9606     } else {\r
9607       *outError = GetLastError();\r
9608     }\r
9609     break;\r
9610 \r
9611   case CPComm:\r
9612     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9613                             &dOutCount, &ovl);\r
9614     if (*outError == NO_ERROR) {\r
9615       outCount = (int) dOutCount;\r
9616     }\r
9617     break;\r
9618   }\r
9619   return outCount;\r
9620 }\r
9621 \r
9622 int\r
9623 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9624                        long msdelay)\r
9625 {\r
9626   /* Ignore delay, not implemented for WinBoard */\r
9627   return OutputToProcess(pr, message, count, outError);\r
9628 }\r
9629 \r
9630 \r
9631 void\r
9632 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9633                         char *buf, int count, int error)\r
9634 {\r
9635   DisplayFatalError(_("Not implemented"), 0, 1);\r
9636 }\r
9637 \r
9638 /* see wgamelist.c for Game List functions */\r
9639 /* see wedittags.c for Edit Tags functions */\r
9640 \r
9641 \r
9642 VOID\r
9643 ICSInitScript()\r
9644 {\r
9645   FILE *f;\r
9646   char buf[MSG_SIZ];\r
9647   char *dummy;\r
9648 \r
9649   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9650     f = fopen(buf, "r");\r
9651     if (f != NULL) {\r
9652       ProcessICSInitScript(f);\r
9653       fclose(f);\r
9654     }\r
9655   }\r
9656 }\r
9657 \r
9658 \r
9659 VOID\r
9660 StartAnalysisClock()\r
9661 {\r
9662   if (analysisTimerEvent) return;\r
9663   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9664                                         (UINT) 2000, NULL);\r
9665 }\r
9666 \r
9667 VOID\r
9668 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9669 {\r
9670   highlightInfo.sq[0].x = fromX;\r
9671   highlightInfo.sq[0].y = fromY;\r
9672   highlightInfo.sq[1].x = toX;\r
9673   highlightInfo.sq[1].y = toY;\r
9674 }\r
9675 \r
9676 VOID\r
9677 ClearHighlights()\r
9678 {\r
9679   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9680     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9681 }\r
9682 \r
9683 VOID\r
9684 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9685 {\r
9686   premoveHighlightInfo.sq[0].x = fromX;\r
9687   premoveHighlightInfo.sq[0].y = fromY;\r
9688   premoveHighlightInfo.sq[1].x = toX;\r
9689   premoveHighlightInfo.sq[1].y = toY;\r
9690 }\r
9691 \r
9692 VOID\r
9693 ClearPremoveHighlights()\r
9694 {\r
9695   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9696     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9697 }\r
9698 \r
9699 VOID\r
9700 ShutDownFrontEnd()\r
9701 {\r
9702   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9703   DeleteClipboardTempFiles();\r
9704 }\r
9705 \r
9706 void\r
9707 BoardToTop()\r
9708 {\r
9709     if (IsIconic(hwndMain))\r
9710       ShowWindow(hwndMain, SW_RESTORE);\r
9711 \r
9712     SetActiveWindow(hwndMain);\r
9713 }\r
9714 \r
9715 /*\r
9716  * Prototypes for animation support routines\r
9717  */\r
9718 static void ScreenSquare(int column, int row, POINT * pt);\r
9719 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9720      POINT frames[], int * nFrames);\r
9721 \r
9722 \r
9723 #define kFactor 4\r
9724 \r
9725 void\r
9726 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9727 {       // [HGM] atomic: animate blast wave\r
9728         int i;\r
9729 \r
9730         explodeInfo.fromX = fromX;\r
9731         explodeInfo.fromY = fromY;\r
9732         explodeInfo.toX = toX;\r
9733         explodeInfo.toY = toY;\r
9734         for(i=1; i<4*kFactor; i++) {\r
9735             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9736             DrawPosition(FALSE, board);\r
9737             Sleep(appData.animSpeed);\r
9738         }\r
9739         explodeInfo.radius = 0;\r
9740         DrawPosition(TRUE, board);\r
9741 }\r
9742 \r
9743 void\r
9744 AnimateMove(board, fromX, fromY, toX, toY)\r
9745      Board board;\r
9746      int fromX;\r
9747      int fromY;\r
9748      int toX;\r
9749      int toY;\r
9750 {\r
9751   ChessSquare piece;\r
9752   POINT start, finish, mid;\r
9753   POINT frames[kFactor * 2 + 1];\r
9754   int nFrames, n;\r
9755 \r
9756   if (!appData.animate) return;\r
9757   if (doingSizing) return;\r
9758   if (fromY < 0 || fromX < 0) return;\r
9759   piece = board[fromY][fromX];\r
9760   if (piece >= EmptySquare) return;\r
9761 \r
9762   ScreenSquare(fromX, fromY, &start);\r
9763   ScreenSquare(toX, toY, &finish);\r
9764 \r
9765   /* All moves except knight jumps move in straight line */\r
9766   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9767     mid.x = start.x + (finish.x - start.x) / 2;\r
9768     mid.y = start.y + (finish.y - start.y) / 2;\r
9769   } else {\r
9770     /* Knight: make straight movement then diagonal */\r
9771     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9772        mid.x = start.x + (finish.x - start.x) / 2;\r
9773        mid.y = start.y;\r
9774      } else {\r
9775        mid.x = start.x;\r
9776        mid.y = start.y + (finish.y - start.y) / 2;\r
9777      }\r
9778   }\r
9779   \r
9780   /* Don't use as many frames for very short moves */\r
9781   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9782     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9783   else\r
9784     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9785 \r
9786   animInfo.from.x = fromX;\r
9787   animInfo.from.y = fromY;\r
9788   animInfo.to.x = toX;\r
9789   animInfo.to.y = toY;\r
9790   animInfo.lastpos = start;\r
9791   animInfo.piece = piece;\r
9792   for (n = 0; n < nFrames; n++) {\r
9793     animInfo.pos = frames[n];\r
9794     DrawPosition(FALSE, NULL);\r
9795     animInfo.lastpos = animInfo.pos;\r
9796     Sleep(appData.animSpeed);\r
9797   }\r
9798   animInfo.pos = finish;\r
9799   DrawPosition(FALSE, NULL);\r
9800   animInfo.piece = EmptySquare;\r
9801   Explode(board, fromX, fromY, toX, toY);\r
9802 }\r
9803 \r
9804 /*      Convert board position to corner of screen rect and color       */\r
9805 \r
9806 static void\r
9807 ScreenSquare(column, row, pt)\r
9808      int column; int row; POINT * pt;\r
9809 {\r
9810   if (flipView) {\r
9811     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9812     pt->y = lineGap + row * (squareSize + lineGap);\r
9813   } else {\r
9814     pt->x = lineGap + column * (squareSize + lineGap);\r
9815     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9816   }\r
9817 }\r
9818 \r
9819 /*      Generate a series of frame coords from start->mid->finish.\r
9820         The movement rate doubles until the half way point is\r
9821         reached, then halves back down to the final destination,\r
9822         which gives a nice slow in/out effect. The algorithmn\r
9823         may seem to generate too many intermediates for short\r
9824         moves, but remember that the purpose is to attract the\r
9825         viewers attention to the piece about to be moved and\r
9826         then to where it ends up. Too few frames would be less\r
9827         noticeable.                                             */\r
9828 \r
9829 static void\r
9830 Tween(start, mid, finish, factor, frames, nFrames)\r
9831      POINT * start; POINT * mid;\r
9832      POINT * finish; int factor;\r
9833      POINT frames[]; int * nFrames;\r
9834 {\r
9835   int n, fraction = 1, count = 0;\r
9836 \r
9837   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9838   for (n = 0; n < factor; n++)\r
9839     fraction *= 2;\r
9840   for (n = 0; n < factor; n++) {\r
9841     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9842     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9843     count ++;\r
9844     fraction = fraction / 2;\r
9845   }\r
9846   \r
9847   /* Midpoint */\r
9848   frames[count] = *mid;\r
9849   count ++;\r
9850   \r
9851   /* Slow out, stepping 1/2, then 1/4, ... */\r
9852   fraction = 2;\r
9853   for (n = 0; n < factor; n++) {\r
9854     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9855     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9856     count ++;\r
9857     fraction = fraction * 2;\r
9858   }\r
9859   *nFrames = count;\r
9860 }\r
9861 \r
9862 void\r
9863 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9864 {\r
9865     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9866 \r
9867     EvalGraphSet( first, last, current, pvInfoList );\r
9868 \r
9869     MakeEngineOutputTitle();\r
9870 }\r
9871 \r
9872 void\r
9873 SettingsPopUp(ChessProgramState *cps)\r
9874 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9875       EngineOptionsPopup(savedHwnd, cps);\r
9876 }\r
9877 \r
9878 int flock(int fid, int code)\r
9879 {\r
9880     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9881     OVERLAPPED ov;\r
9882     ov.hEvent = NULL;\r
9883     ov.Offset = 0;\r
9884     ov.OffsetHigh = 0;\r
9885     switch(code) {\r
9886       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9887       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9888       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9889       default: return -1;\r
9890     }\r
9891     return 0;\r
9892 }\r