Allow switching off match mode through menu
[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[][40] = {\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, IDOK, IDCANCEL }, \r
257 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
258   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
259 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
260 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
261   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
262 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
263 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
264   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
265 { ABOUTBOX2, IDC_ChessBoard }, \r
266 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
267   OPT_GameListClose, IDC_GameListDoFilter }, \r
268 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
269 { DLG_Error, IDOK }, \r
270 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
271   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
272 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
273 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
274   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
275   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
276 { DLG_IndexNumber, IDC_Index }, \r
277 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
278 { DLG_TypeInName, IDOK, IDCANCEL }, \r
279 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
280   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
281 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
282   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
283   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
284   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
285   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
286   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
287   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
288 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
289   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
290   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
291   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
292   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
293   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
294   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
295   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
296   GPB_General, GPB_Alarm }, \r
297 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
298   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
299   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
300   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
301   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
302   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
303   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
304   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
305 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
306   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
307   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
308   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
309   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
310   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
311   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
312   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
313   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
314 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
315   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
316   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
317   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
318 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
319 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
320   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
321 { DLG_MoveHistory }, \r
322 { DLG_EvalGraph }, \r
323 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
324 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
325 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
326   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
327   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
328   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
329 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
330   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
331   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
332 { 0 }\r
333 };\r
334 \r
335 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
336 static int lastChecked;\r
337 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
338 extern int tinyLayout;\r
339 extern char * menuBarText[][10];\r
340 \r
341 void\r
342 LoadLanguageFile(char *name)\r
343 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
344     FILE *f;\r
345     int i=0, j=0, n=0, k;\r
346     char buf[MSG_SIZ];\r
347 \r
348     if(!name || name[0] == NULLCHAR) return;\r
349       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
350     appData.language = oldLanguage;\r
351     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
352     if((f = fopen(buf, "r")) == NULL) return;\r
353     while((k = fgetc(f)) != EOF) {\r
354         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
355         languageBuf[i] = k;\r
356         if(k == '\n') {\r
357             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
358                 char *p;\r
359                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
360                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
361                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
362                         english[j] = languageBuf + n + 1; *p = 0;\r
363                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
364 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
365                     }\r
366                 }\r
367             }\r
368             n = i + 1;\r
369         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
370             switch(k) {\r
371               case 'n': k = '\n'; break;\r
372               case 'r': k = '\r'; break;\r
373               case 't': k = '\t'; break;\r
374             }\r
375             languageBuf[--i] = k;\r
376         }\r
377         i++;\r
378     }\r
379     fclose(f);\r
380     barbaric = (j != 0);\r
381     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
382 }\r
383 \r
384 char *\r
385 T_(char *s)\r
386 {   // return the translation of the given string\r
387     // efficiency can be improved a lot...\r
388     int i=0;\r
389 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
390     if(!barbaric) return s;\r
391     if(!s) return ""; // sanity\r
392     while(english[i]) {\r
393         if(!strcmp(s, english[i])) return foreign[i];\r
394         i++;\r
395     }\r
396     return s;\r
397 }\r
398 \r
399 void\r
400 Translate(HWND hDlg, int dialogID)\r
401 {   // translate all text items in the given dialog\r
402     int i=0, j, k;\r
403     char buf[MSG_SIZ], *s;\r
404     if(!barbaric) return;\r
405     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
406     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
407     GetWindowText( hDlg, buf, MSG_SIZ );\r
408     s = T_(buf);\r
409     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
410     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
411         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
412         if(strlen(buf) == 0) continue;\r
413         s = T_(buf);\r
414         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
415     }\r
416 }\r
417 \r
418 HMENU\r
419 TranslateOneMenu(int i, HMENU subMenu)\r
420 {\r
421     int j;\r
422     static MENUITEMINFO info;\r
423 \r
424     info.cbSize = sizeof(MENUITEMINFO);\r
425     info.fMask = MIIM_STATE | MIIM_TYPE;\r
426           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
427             char buf[MSG_SIZ];\r
428             info.dwTypeData = buf;\r
429             info.cch = sizeof(buf);\r
430             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
431             if(i < 10) {
432                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
433                 else menuText[i][j] = strdup(buf); // remember original on first change\r
434             }\r
435             if(buf[0] == NULLCHAR) continue;\r
436             info.dwTypeData = T_(buf);\r
437             info.cch = strlen(buf)+1;\r
438             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
439           }\r
440     return subMenu;\r
441 }\r
442 \r
443 void\r
444 TranslateMenus(int addLanguage)\r
445 {\r
446     int i;\r
447     WIN32_FIND_DATA fileData;\r
448     HANDLE hFind;\r
449 #define IDM_English 1970\r
450     if(1) {\r
451         HMENU mainMenu = GetMenu(hwndMain);\r
452         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
453           HMENU subMenu = GetSubMenu(mainMenu, i);\r
454           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
455                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
456           TranslateOneMenu(i, subMenu);\r
457         }\r
458         DrawMenuBar(hwndMain);\r
459     }\r
460 \r
461     if(!addLanguage) return;\r
462     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
463         HMENU mainMenu = GetMenu(hwndMain);\r
464         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
465         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
466         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
467         i = 0; lastChecked = IDM_English;\r
468         do {\r
469             char *p, *q = fileData.cFileName;\r
470             int checkFlag = MF_UNCHECKED;\r
471             languageFile[i] = strdup(q);\r
472             if(barbaric && !strcmp(oldLanguage, q)) {\r
473                 checkFlag = MF_CHECKED;\r
474                 lastChecked = IDM_English + i + 1;\r
475                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
476             }\r
477             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
478             p = strstr(fileData.cFileName, ".lng");\r
479             if(p) *p = 0;\r
480             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
481         } while(FindNextFile(hFind, &fileData));\r
482         FindClose(hFind);\r
483     }\r
484 }\r
485 \r
486 #endif\r
487 \r
488 typedef struct {\r
489   char *name;\r
490   int squareSize;\r
491   int lineGap;\r
492   int smallLayout;\r
493   int tinyLayout;\r
494   int cliWidth, cliHeight;\r
495 } SizeInfo;\r
496 \r
497 SizeInfo sizeInfo[] = \r
498 {\r
499   { "tiny",     21, 0, 1, 1, 0, 0 },\r
500   { "teeny",    25, 1, 1, 1, 0, 0 },\r
501   { "dinky",    29, 1, 1, 1, 0, 0 },\r
502   { "petite",   33, 1, 1, 1, 0, 0 },\r
503   { "slim",     37, 2, 1, 0, 0, 0 },\r
504   { "small",    40, 2, 1, 0, 0, 0 },\r
505   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
506   { "middling", 49, 2, 0, 0, 0, 0 },\r
507   { "average",  54, 2, 0, 0, 0, 0 },\r
508   { "moderate", 58, 3, 0, 0, 0, 0 },\r
509   { "medium",   64, 3, 0, 0, 0, 0 },\r
510   { "bulky",    72, 3, 0, 0, 0, 0 },\r
511   { "large",    80, 3, 0, 0, 0, 0 },\r
512   { "big",      87, 3, 0, 0, 0, 0 },\r
513   { "huge",     95, 3, 0, 0, 0, 0 },\r
514   { "giant",    108, 3, 0, 0, 0, 0 },\r
515   { "colossal", 116, 4, 0, 0, 0, 0 },\r
516   { "titanic",  129, 4, 0, 0, 0, 0 },\r
517   { NULL, 0, 0, 0, 0, 0, 0 }\r
518 };\r
519 \r
520 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
521 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
522 {\r
523   { 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) },\r
524   { 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) },\r
525   { 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) },\r
526   { 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) },\r
527   { 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) },\r
528   { 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) },\r
529   { 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) },\r
530   { 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) },\r
531   { 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) },\r
532   { 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) },\r
533   { 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) },\r
534   { 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) },\r
535   { 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) },\r
536   { 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) },\r
537   { 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) },\r
538   { 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) },\r
539   { 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) },\r
540   { 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) },\r
541 };\r
542 \r
543 MyFont *font[NUM_SIZES][NUM_FONTS];\r
544 \r
545 typedef struct {\r
546   char *label;\r
547   int id;\r
548   HWND hwnd;\r
549   WNDPROC wndproc;\r
550 } MyButtonDesc;\r
551 \r
552 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
553 #define N_BUTTONS 5\r
554 \r
555 MyButtonDesc buttonDesc[N_BUTTONS] =\r
556 {\r
557   {"<<", IDM_ToStart, NULL, NULL},\r
558   {"<", IDM_Backward, NULL, NULL},\r
559   {"P", IDM_Pause, NULL, NULL},\r
560   {">", IDM_Forward, NULL, NULL},\r
561   {">>", IDM_ToEnd, NULL, NULL},\r
562 };\r
563 \r
564 int tinyLayout = 0, smallLayout = 0;\r
565 #define MENU_BAR_ITEMS 9\r
566 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
567   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
568   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
569 };\r
570 \r
571 \r
572 MySound sounds[(int)NSoundClasses];\r
573 MyTextAttribs textAttribs[(int)NColorClasses];\r
574 \r
575 MyColorizeAttribs colorizeAttribs[] = {\r
576   { (COLORREF)0, 0, N_("Shout Text") },\r
577   { (COLORREF)0, 0, N_("SShout/CShout") },\r
578   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
579   { (COLORREF)0, 0, N_("Channel Text") },\r
580   { (COLORREF)0, 0, N_("Kibitz Text") },\r
581   { (COLORREF)0, 0, N_("Tell Text") },\r
582   { (COLORREF)0, 0, N_("Challenge Text") },\r
583   { (COLORREF)0, 0, N_("Request Text") },\r
584   { (COLORREF)0, 0, N_("Seek Text") },\r
585   { (COLORREF)0, 0, N_("Normal Text") },\r
586   { (COLORREF)0, 0, N_("None") }\r
587 };\r
588 \r
589 \r
590 \r
591 static char *commentTitle;\r
592 static char *commentText;\r
593 static int commentIndex;\r
594 static Boolean editComment = FALSE;\r
595 \r
596 \r
597 char errorTitle[MSG_SIZ];\r
598 char errorMessage[2*MSG_SIZ];\r
599 HWND errorDialog = NULL;\r
600 BOOLEAN moveErrorMessageUp = FALSE;\r
601 BOOLEAN consoleEcho = TRUE;\r
602 CHARFORMAT consoleCF;\r
603 COLORREF consoleBackgroundColor;\r
604 \r
605 char *programVersion;\r
606 \r
607 #define CPReal 1\r
608 #define CPComm 2\r
609 #define CPSock 3\r
610 #define CPRcmd 4\r
611 typedef int CPKind;\r
612 \r
613 typedef struct {\r
614   CPKind kind;\r
615   HANDLE hProcess;\r
616   DWORD pid;\r
617   HANDLE hTo;\r
618   HANDLE hFrom;\r
619   SOCKET sock;\r
620   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
621 } ChildProc;\r
622 \r
623 #define INPUT_SOURCE_BUF_SIZE 4096\r
624 \r
625 typedef struct _InputSource {\r
626   CPKind kind;\r
627   HANDLE hFile;\r
628   SOCKET sock;\r
629   int lineByLine;\r
630   HANDLE hThread;\r
631   DWORD id;\r
632   char buf[INPUT_SOURCE_BUF_SIZE];\r
633   char *next;\r
634   DWORD count;\r
635   int error;\r
636   InputCallback func;\r
637   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
638   VOIDSTAR closure;\r
639 } InputSource;\r
640 \r
641 InputSource *consoleInputSource;\r
642 \r
643 DCB dcb;\r
644 \r
645 /* forward */\r
646 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
647 VOID ConsoleCreate();\r
648 LRESULT CALLBACK\r
649   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
650 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
651 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
652 VOID ParseCommSettings(char *arg, DCB *dcb);\r
653 LRESULT CALLBACK\r
654   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
655 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
656 void ParseIcsTextMenu(char *icsTextMenuString);\r
657 VOID PopUpNameDialog(char firstchar);\r
658 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
659 \r
660 /* [AS] */\r
661 int NewGameFRC();\r
662 int GameListOptions();\r
663 \r
664 int dummy; // [HGM] for obsolete args\r
665 \r
666 HWND hwndMain = NULL;        /* root window*/\r
667 HWND hwndConsole = NULL;\r
668 HWND commentDialog = NULL;\r
669 HWND moveHistoryDialog = NULL;\r
670 HWND evalGraphDialog = NULL;\r
671 HWND engineOutputDialog = NULL;\r
672 HWND gameListDialog = NULL;\r
673 HWND editTagsDialog = NULL;\r
674 \r
675 int commentUp = FALSE;\r
676 \r
677 WindowPlacement wpMain;\r
678 WindowPlacement wpConsole;\r
679 WindowPlacement wpComment;\r
680 WindowPlacement wpMoveHistory;\r
681 WindowPlacement wpEvalGraph;\r
682 WindowPlacement wpEngineOutput;\r
683 WindowPlacement wpGameList;\r
684 WindowPlacement wpTags;\r
685 \r
686 VOID EngineOptionsPopup(); // [HGM] settings\r
687 \r
688 VOID GothicPopUp(char *title, VariantClass variant);\r
689 /*\r
690  * Setting "frozen" should disable all user input other than deleting\r
691  * the window.  We do this while engines are initializing themselves.\r
692  */\r
693 static int frozen = 0;\r
694 static int oldMenuItemState[MENU_BAR_ITEMS];\r
695 void FreezeUI()\r
696 {\r
697   HMENU hmenu;\r
698   int i;\r
699 \r
700   if (frozen) return;\r
701   frozen = 1;\r
702   hmenu = GetMenu(hwndMain);\r
703   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
704     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
705   }\r
706   DrawMenuBar(hwndMain);\r
707 }\r
708 \r
709 /* Undo a FreezeUI */\r
710 void ThawUI()\r
711 {\r
712   HMENU hmenu;\r
713   int i;\r
714 \r
715   if (!frozen) return;\r
716   frozen = 0;\r
717   hmenu = GetMenu(hwndMain);\r
718   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
719     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
720   }\r
721   DrawMenuBar(hwndMain);\r
722 }\r
723 \r
724 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
725 \r
726 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
727 #ifdef JAWS\r
728 #include "jaws.c"\r
729 #else\r
730 #define JAWS_INIT\r
731 #define JAWS_ARGS\r
732 #define JAWS_ALT_INTERCEPT\r
733 #define JAWS_KB_NAVIGATION\r
734 #define JAWS_MENU_ITEMS\r
735 #define JAWS_SILENCE\r
736 #define JAWS_REPLAY\r
737 #define JAWS_ACCEL\r
738 #define JAWS_COPYRIGHT\r
739 #define JAWS_DELETE(X) X\r
740 #define SAYMACHINEMOVE()\r
741 #define SAY(X)\r
742 #endif\r
743 \r
744 /*---------------------------------------------------------------------------*\\r
745  *\r
746  * WinMain\r
747  *\r
748 \*---------------------------------------------------------------------------*/\r
749 \r
750 int APIENTRY\r
751 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
752         LPSTR lpCmdLine, int nCmdShow)\r
753 {\r
754   MSG msg;\r
755   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
756 //  INITCOMMONCONTROLSEX ex;\r
757 \r
758   debugFP = stderr;\r
759 \r
760   LoadLibrary("RICHED32.DLL");\r
761   consoleCF.cbSize = sizeof(CHARFORMAT);\r
762 \r
763   if (!InitApplication(hInstance)) {\r
764     return (FALSE);\r
765   }\r
766   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
767     return (FALSE);\r
768   }\r
769 \r
770   JAWS_INIT\r
771   TranslateMenus(1);\r
772 \r
773 //  InitCommonControlsEx(&ex);\r
774   InitCommonControls();\r
775 \r
776   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
777   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
778   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
779 \r
780   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
781 \r
782   while (GetMessage(&msg, /* message structure */\r
783                     NULL, /* handle of window receiving the message */\r
784                     0,    /* lowest message to examine */\r
785                     0))   /* highest message to examine */\r
786     {\r
787 \r
788       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
789         // [HGM] navigate: switch between all windows with tab\r
790         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
791         int i, currentElement = 0;\r
792 \r
793         // first determine what element of the chain we come from (if any)\r
794         if(appData.icsActive) {\r
795             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
796             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
797         }\r
798         if(engineOutputDialog && EngineOutputIsUp()) {\r
799             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
800             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
801         }\r
802         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
803             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
804         }\r
805         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
806         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
807         if(msg.hwnd == e1)                 currentElement = 2; else\r
808         if(msg.hwnd == e2)                 currentElement = 3; else\r
809         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
810         if(msg.hwnd == mh)                currentElement = 4; else\r
811         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
812         if(msg.hwnd == hText)  currentElement = 5; else\r
813         if(msg.hwnd == hInput) currentElement = 6; else\r
814         for (i = 0; i < N_BUTTONS; i++) {\r
815             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
816         }\r
817 \r
818         // determine where to go to\r
819         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
820           do {\r
821             currentElement = (currentElement + direction) % 7;\r
822             switch(currentElement) {\r
823                 case 0:\r
824                   h = hwndMain; break; // passing this case always makes the loop exit\r
825                 case 1:\r
826                   h = buttonDesc[0].hwnd; break; // could be NULL\r
827                 case 2:\r
828                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
829                   h = e1; break;\r
830                 case 3:\r
831                   if(!EngineOutputIsUp()) continue;\r
832                   h = e2; break;\r
833                 case 4:\r
834                   if(!MoveHistoryIsUp()) continue;\r
835                   h = mh; break;\r
836 //              case 6: // input to eval graph does not seem to get here!\r
837 //                if(!EvalGraphIsUp()) continue;\r
838 //                h = evalGraphDialog; break;\r
839                 case 5:\r
840                   if(!appData.icsActive) continue;\r
841                   SAY("display");\r
842                   h = hText; break;\r
843                 case 6:\r
844                   if(!appData.icsActive) continue;\r
845                   SAY("input");\r
846                   h = hInput; break;\r
847             }\r
848           } while(h == 0);\r
849 \r
850           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
851           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
852           SetFocus(h);\r
853 \r
854           continue; // this message now has been processed\r
855         }\r
856       }\r
857 \r
858       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
859           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
860           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
861           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
862           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
863           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
864           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
865           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
866           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
867           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
868         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
869         for(i=0; i<MAX_CHAT; i++) \r
870             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
871                 done = 1; break;\r
872         }\r
873         if(done) continue; // [HGM] chat: end patch\r
874         TranslateMessage(&msg); /* Translates virtual key codes */\r
875         DispatchMessage(&msg);  /* Dispatches message to window */\r
876       }\r
877     }\r
878 \r
879 \r
880   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
881 }\r
882 \r
883 /*---------------------------------------------------------------------------*\\r
884  *\r
885  * Initialization functions\r
886  *\r
887 \*---------------------------------------------------------------------------*/\r
888 \r
889 void\r
890 SetUserLogo()\r
891 {   // update user logo if necessary\r
892     static char oldUserName[MSG_SIZ], *curName;\r
893 \r
894     if(appData.autoLogo) {\r
895           curName = UserName();\r
896           if(strcmp(curName, oldUserName)) {\r
897             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
898                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
899                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
900                 if(userLogo == NULL)\r
901                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
902           }\r
903     }\r
904 }\r
905 \r
906 BOOL\r
907 InitApplication(HINSTANCE hInstance)\r
908 {\r
909   WNDCLASS wc;\r
910 \r
911   /* Fill in window class structure with parameters that describe the */\r
912   /* main window. */\r
913 \r
914   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
915   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
916   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
917   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
918   wc.hInstance     = hInstance;         /* Owner of this class */\r
919   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
920   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
921   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
922   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
923   wc.lpszClassName = szAppName;                 /* Name to register as */\r
924 \r
925   /* Register the window class and return success/failure code. */\r
926   if (!RegisterClass(&wc)) return FALSE;\r
927 \r
928   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
929   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
930   wc.cbClsExtra    = 0;\r
931   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
932   wc.hInstance     = hInstance;\r
933   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
934   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
935   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
936   wc.lpszMenuName  = NULL;\r
937   wc.lpszClassName = szConsoleName;\r
938 \r
939   if (!RegisterClass(&wc)) return FALSE;\r
940   return TRUE;\r
941 }\r
942 \r
943 \r
944 /* Set by InitInstance, used by EnsureOnScreen */\r
945 int screenHeight, screenWidth;\r
946 \r
947 void\r
948 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
949 {\r
950 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
951   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
952   if (*x > screenWidth - 32) *x = 0;\r
953   if (*y > screenHeight - 32) *y = 0;\r
954   if (*x < minX) *x = minX;\r
955   if (*y < minY) *y = minY;\r
956 }\r
957 \r
958 BOOL\r
959 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
960 {\r
961   HWND hwnd; /* Main window handle. */\r
962   int ibs;\r
963   WINDOWPLACEMENT wp;\r
964   char *filepart;\r
965 \r
966   hInst = hInstance;    /* Store instance handle in our global variable */\r
967   programName = szAppName;\r
968 \r
969   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
970     *filepart = NULLCHAR;\r
971   } else {\r
972     GetCurrentDirectory(MSG_SIZ, installDir);\r
973   }\r
974   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
975   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
976   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
977   /* xboard, and older WinBoards, controlled the move sound with the\r
978      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
979      always turn the option on (so that the backend will call us),\r
980      then let the user turn the sound off by setting it to silence if\r
981      desired.  To accommodate old winboard.ini files saved by old\r
982      versions of WinBoard, we also turn off the sound if the option\r
983      was initially set to false. [HGM] taken out of InitAppData */\r
984   if (!appData.ringBellAfterMoves) {\r
985     sounds[(int)SoundMove].name = strdup("");\r
986     appData.ringBellAfterMoves = TRUE;\r
987   }\r
988   if (appData.debugMode) {\r
989     debugFP = fopen(appData.nameOfDebugFile, "w");\r
990     setbuf(debugFP, NULL);\r
991   }\r
992 \r
993   LoadLanguageFile(appData.language);\r
994 \r
995   InitBackEnd1();\r
996 \r
997 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
998 //  InitEngineUCI( installDir, &second );\r
999 \r
1000   /* Create a main window for this application instance. */\r
1001   hwnd = CreateWindow(szAppName, szTitle,\r
1002                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1003                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1004                       NULL, NULL, hInstance, NULL);\r
1005   hwndMain = hwnd;\r
1006 \r
1007   /* If window could not be created, return "failure" */\r
1008   if (!hwnd) {\r
1009     return (FALSE);\r
1010   }\r
1011 \r
1012   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1013   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
1014       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1015 \r
1016       if (first.programLogo == NULL && appData.debugMode) {\r
1017           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1018       }\r
1019   } else if(appData.autoLogo) {\r
1020       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1021         char buf[MSG_SIZ];\r
1022           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1023         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1024       }\r
1025   }\r
1026 \r
1027   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1028       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1029 \r
1030       if (second.programLogo == NULL && appData.debugMode) {\r
1031           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1032       }\r
1033   } else if(appData.autoLogo) {\r
1034       char buf[MSG_SIZ];\r
1035       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1036         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1037         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1038       } else\r
1039       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1040         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1041         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1042       }\r
1043   }\r
1044 \r
1045   SetUserLogo();\r
1046 \r
1047   iconWhite = LoadIcon(hInstance, "icon_white");\r
1048   iconBlack = LoadIcon(hInstance, "icon_black");\r
1049   iconCurrent = iconWhite;\r
1050   InitDrawingColors();\r
1051   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1052   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1053   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1054     /* Compute window size for each board size, and use the largest\r
1055        size that fits on this screen as the default. */\r
1056     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1057     if (boardSize == (BoardSize)-1 &&\r
1058         winH <= screenHeight\r
1059            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1060         && winW <= screenWidth) {\r
1061       boardSize = (BoardSize)ibs;\r
1062     }\r
1063   }\r
1064 \r
1065   InitDrawingSizes(boardSize, 0);\r
1066   InitMenuChecks();\r
1067   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1068 \r
1069   /* [AS] Load textures if specified */\r
1070   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1071   \r
1072   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1073       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1074       liteBackTextureMode = appData.liteBackTextureMode;\r
1075 \r
1076       if (liteBackTexture == NULL && appData.debugMode) {\r
1077           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1078       }\r
1079   }\r
1080   \r
1081   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1082       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1083       darkBackTextureMode = appData.darkBackTextureMode;\r
1084 \r
1085       if (darkBackTexture == NULL && appData.debugMode) {\r
1086           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1087       }\r
1088   }\r
1089 \r
1090   mysrandom( (unsigned) time(NULL) );\r
1091 \r
1092   /* [AS] Restore layout */\r
1093   if( wpMoveHistory.visible ) {\r
1094       MoveHistoryPopUp();\r
1095   }\r
1096 \r
1097   if( wpEvalGraph.visible ) {\r
1098       EvalGraphPopUp();\r
1099   }\r
1100 \r
1101   if( wpEngineOutput.visible ) {\r
1102       EngineOutputPopUp();\r
1103   }\r
1104 \r
1105   /* Make the window visible; update its client area; and return "success" */\r
1106   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1107   wp.length = sizeof(WINDOWPLACEMENT);\r
1108   wp.flags = 0;\r
1109   wp.showCmd = nCmdShow;\r
1110   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1111   wp.rcNormalPosition.left = wpMain.x;\r
1112   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1113   wp.rcNormalPosition.top = wpMain.y;\r
1114   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1115   SetWindowPlacement(hwndMain, &wp);\r
1116 \r
1117   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1118 \r
1119   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1120                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1121 \r
1122   if (hwndConsole) {\r
1123 #if AOT_CONSOLE\r
1124     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1125                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1126 #endif\r
1127     ShowWindow(hwndConsole, nCmdShow);\r
1128     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1129       char buf[MSG_SIZ], *p = buf, *q;\r
1130         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1131       do {\r
1132         q = strchr(p, ';');\r
1133         if(q) *q++ = 0;\r
1134         if(*p) ChatPopUp(p);\r
1135       } while(p=q);\r
1136     }\r
1137     SetActiveWindow(hwndConsole);\r
1138   }\r
1139   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1140   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1141 \r
1142   return TRUE;\r
1143 \r
1144 }\r
1145 \r
1146 VOID\r
1147 InitMenuChecks()\r
1148 {\r
1149   HMENU hmenu = GetMenu(hwndMain);\r
1150 \r
1151   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1152                         MF_BYCOMMAND|((appData.icsActive &&\r
1153                                        *appData.icsCommPort != NULLCHAR) ?\r
1154                                       MF_ENABLED : MF_GRAYED));\r
1155   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1156                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1157                                      MF_CHECKED : MF_UNCHECKED));\r
1158 }\r
1159 \r
1160 //---------------------------------------------------------------------------------------------------------\r
1161 \r
1162 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1163 #define XBOARD FALSE\r
1164 \r
1165 #define OPTCHAR "/"\r
1166 #define SEPCHAR "="\r
1167 \r
1168 #include "args.h"\r
1169 \r
1170 // front-end part of option handling\r
1171 \r
1172 VOID\r
1173 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1174 {\r
1175   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1176   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1177   DeleteDC(hdc);\r
1178   lf->lfWidth = 0;\r
1179   lf->lfEscapement = 0;\r
1180   lf->lfOrientation = 0;\r
1181   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1182   lf->lfItalic = mfp->italic;\r
1183   lf->lfUnderline = mfp->underline;\r
1184   lf->lfStrikeOut = mfp->strikeout;\r
1185   lf->lfCharSet = mfp->charset;\r
1186   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1187   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1188   lf->lfQuality = DEFAULT_QUALITY;\r
1189   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1190     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1191 }\r
1192 \r
1193 void\r
1194 CreateFontInMF(MyFont *mf)\r
1195\r
1196   LFfromMFP(&mf->lf, &mf->mfp);\r
1197   if (mf->hf) DeleteObject(mf->hf);\r
1198   mf->hf = CreateFontIndirect(&mf->lf);\r
1199 }\r
1200 \r
1201 // [HGM] This platform-dependent table provides the location for storing the color info\r
1202 void *\r
1203 colorVariable[] = {\r
1204   &whitePieceColor, \r
1205   &blackPieceColor, \r
1206   &lightSquareColor,\r
1207   &darkSquareColor, \r
1208   &highlightSquareColor,\r
1209   &premoveHighlightColor,\r
1210   NULL,\r
1211   &consoleBackgroundColor,\r
1212   &appData.fontForeColorWhite,\r
1213   &appData.fontBackColorWhite,\r
1214   &appData.fontForeColorBlack,\r
1215   &appData.fontBackColorBlack,\r
1216   &appData.evalHistColorWhite,\r
1217   &appData.evalHistColorBlack,\r
1218   &appData.highlightArrowColor,\r
1219 };\r
1220 \r
1221 /* Command line font name parser.  NULL name means do nothing.\r
1222    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1223    For backward compatibility, syntax without the colon is also\r
1224    accepted, but font names with digits in them won't work in that case.\r
1225 */\r
1226 VOID\r
1227 ParseFontName(char *name, MyFontParams *mfp)\r
1228 {\r
1229   char *p, *q;\r
1230   if (name == NULL) return;\r
1231   p = name;\r
1232   q = strchr(p, ':');\r
1233   if (q) {\r
1234     if (q - p >= sizeof(mfp->faceName))\r
1235       ExitArgError(_("Font name too long:"), name);\r
1236     memcpy(mfp->faceName, p, q - p);\r
1237     mfp->faceName[q - p] = NULLCHAR;\r
1238     p = q + 1;\r
1239   } else {\r
1240     q = mfp->faceName;\r
1241     while (*p && !isdigit(*p)) {\r
1242       *q++ = *p++;\r
1243       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1244         ExitArgError(_("Font name too long:"), name);\r
1245     }\r
1246     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1247     *q = NULLCHAR;\r
1248   }\r
1249   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1250   mfp->pointSize = (float) atof(p);\r
1251   mfp->bold = (strchr(p, 'b') != NULL);\r
1252   mfp->italic = (strchr(p, 'i') != NULL);\r
1253   mfp->underline = (strchr(p, 'u') != NULL);\r
1254   mfp->strikeout = (strchr(p, 's') != NULL);\r
1255   mfp->charset = DEFAULT_CHARSET;\r
1256   q = strchr(p, 'c');\r
1257   if (q)\r
1258     mfp->charset = (BYTE) atoi(q+1);\r
1259 }\r
1260 \r
1261 void\r
1262 ParseFont(char *name, int number)\r
1263 { // wrapper to shield back-end from 'font'\r
1264   ParseFontName(name, &font[boardSize][number]->mfp);\r
1265 }\r
1266 \r
1267 void\r
1268 SetFontDefaults()\r
1269 { // in WB  we have a 2D array of fonts; this initializes their description\r
1270   int i, j;\r
1271   /* Point font array elements to structures and\r
1272      parse default font names */\r
1273   for (i=0; i<NUM_FONTS; i++) {\r
1274     for (j=0; j<NUM_SIZES; j++) {\r
1275       font[j][i] = &fontRec[j][i];\r
1276       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1277     }\r
1278   }\r
1279 }\r
1280 \r
1281 void\r
1282 CreateFonts()\r
1283 { // here we create the actual fonts from the selected descriptions\r
1284   int i, j;\r
1285   for (i=0; i<NUM_FONTS; i++) {\r
1286     for (j=0; j<NUM_SIZES; j++) {\r
1287       CreateFontInMF(font[j][i]);\r
1288     }\r
1289   }\r
1290 }\r
1291 /* Color name parser.\r
1292    X version accepts X color names, but this one\r
1293    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1294 COLORREF\r
1295 ParseColorName(char *name)\r
1296 {\r
1297   int red, green, blue, count;\r
1298   char buf[MSG_SIZ];\r
1299 \r
1300   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1301   if (count != 3) {\r
1302     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1303       &red, &green, &blue);\r
1304   }\r
1305   if (count != 3) {\r
1306     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1307     DisplayError(buf, 0);\r
1308     return RGB(0, 0, 0);\r
1309   }\r
1310   return PALETTERGB(red, green, blue);\r
1311 }\r
1312 \r
1313 void\r
1314 ParseColor(int n, char *name)\r
1315 { // for WinBoard the color is an int, which needs to be derived from the string\r
1316   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1317 }\r
1318 \r
1319 void\r
1320 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1321 {\r
1322   char *e = argValue;\r
1323   int eff = 0;\r
1324 \r
1325   while (*e) {\r
1326     if (*e == 'b')      eff |= CFE_BOLD;\r
1327     else if (*e == 'i') eff |= CFE_ITALIC;\r
1328     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1329     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1330     else if (*e == '#' || isdigit(*e)) break;\r
1331     e++;\r
1332   }\r
1333   *effects = eff;\r
1334   *color   = ParseColorName(e);\r
1335 }\r
1336 \r
1337 void\r
1338 ParseTextAttribs(ColorClass cc, char *s)\r
1339 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1340     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1341     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1342 }\r
1343 \r
1344 void\r
1345 ParseBoardSize(void *addr, char *name)\r
1346 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1347   BoardSize bs = SizeTiny;\r
1348   while (sizeInfo[bs].name != NULL) {\r
1349     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1350         *(BoardSize *)addr = bs;\r
1351         return;\r
1352     }\r
1353     bs++;\r
1354   }\r
1355   ExitArgError(_("Unrecognized board size value"), name);\r
1356 }\r
1357 \r
1358 void\r
1359 LoadAllSounds()\r
1360 { // [HGM] import name from appData first\r
1361   ColorClass cc;\r
1362   SoundClass sc;\r
1363   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1364     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1365     textAttribs[cc].sound.data = NULL;\r
1366     MyLoadSound(&textAttribs[cc].sound);\r
1367   }\r
1368   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1369     textAttribs[cc].sound.name = strdup("");\r
1370     textAttribs[cc].sound.data = NULL;\r
1371   }\r
1372   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1373     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1374     sounds[sc].data = NULL;\r
1375     MyLoadSound(&sounds[sc]);\r
1376   }\r
1377 }\r
1378 \r
1379 void\r
1380 SetCommPortDefaults()\r
1381 {\r
1382    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1383   dcb.DCBlength = sizeof(DCB);\r
1384   dcb.BaudRate = 9600;\r
1385   dcb.fBinary = TRUE;\r
1386   dcb.fParity = FALSE;\r
1387   dcb.fOutxCtsFlow = FALSE;\r
1388   dcb.fOutxDsrFlow = FALSE;\r
1389   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1390   dcb.fDsrSensitivity = FALSE;\r
1391   dcb.fTXContinueOnXoff = TRUE;\r
1392   dcb.fOutX = FALSE;\r
1393   dcb.fInX = FALSE;\r
1394   dcb.fNull = FALSE;\r
1395   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1396   dcb.fAbortOnError = FALSE;\r
1397   dcb.ByteSize = 7;\r
1398   dcb.Parity = SPACEPARITY;\r
1399   dcb.StopBits = ONESTOPBIT;\r
1400 }\r
1401 \r
1402 // [HGM] args: these three cases taken out to stay in front-end\r
1403 void\r
1404 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1405 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1406         // while the curent board size determines the element. This system should be ported to XBoard.\r
1407         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1408         int bs;\r
1409         for (bs=0; bs<NUM_SIZES; bs++) {\r
1410           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1411           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1412           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1413             ad->argName, mfp->faceName, mfp->pointSize,\r
1414             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1415             mfp->bold ? "b" : "",\r
1416             mfp->italic ? "i" : "",\r
1417             mfp->underline ? "u" : "",\r
1418             mfp->strikeout ? "s" : "",\r
1419             (int)mfp->charset);\r
1420         }\r
1421       }\r
1422 \r
1423 void\r
1424 ExportSounds()\r
1425 { // [HGM] copy the names from the internal WB variables to appData\r
1426   ColorClass cc;\r
1427   SoundClass sc;\r
1428   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1429     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1430   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1431     (&appData.soundMove)[sc] = sounds[sc].name;\r
1432 }\r
1433 \r
1434 void\r
1435 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1436 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1437         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1438         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1439           (ta->effects & CFE_BOLD) ? "b" : "",\r
1440           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1441           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1442           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1443           (ta->effects) ? " " : "",\r
1444           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1445       }\r
1446 \r
1447 void\r
1448 SaveColor(FILE *f, ArgDescriptor *ad)\r
1449 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1450         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1451         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1452           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1453 }\r
1454 \r
1455 void\r
1456 SaveBoardSize(FILE *f, char *name, void *addr)\r
1457 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1458   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1459 }\r
1460 \r
1461 void\r
1462 ParseCommPortSettings(char *s)\r
1463 { // wrapper to keep dcb from back-end\r
1464   ParseCommSettings(s, &dcb);\r
1465 }\r
1466 \r
1467 void\r
1468 GetWindowCoords()\r
1469 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1470   GetActualPlacement(hwndMain, &wpMain);\r
1471   GetActualPlacement(hwndConsole, &wpConsole);\r
1472   GetActualPlacement(commentDialog, &wpComment);\r
1473   GetActualPlacement(editTagsDialog, &wpTags);\r
1474   GetActualPlacement(gameListDialog, &wpGameList);\r
1475   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1476   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1477   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1478 }\r
1479 \r
1480 void\r
1481 PrintCommPortSettings(FILE *f, char *name)\r
1482 { // wrapper to shield back-end from DCB\r
1483       PrintCommSettings(f, name, &dcb);\r
1484 }\r
1485 \r
1486 int\r
1487 MySearchPath(char *installDir, char *name, char *fullname)\r
1488 {\r
1489   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1490   if(name[0]== '%') {\r
1491     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1492     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1493       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1494       *strchr(buf, '%') = 0;\r
1495       strcat(fullname, getenv(buf));\r
1496       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1497     }\r
1498     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1499     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1500     return (int) strlen(fullname);\r
1501   }\r
1502   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1503 }\r
1504 \r
1505 int\r
1506 MyGetFullPathName(char *name, char *fullname)\r
1507 {\r
1508   char *dummy;\r
1509   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1510 }\r
1511 \r
1512 int\r
1513 MainWindowUp()\r
1514 { // [HGM] args: allows testing if main window is realized from back-end\r
1515   return hwndMain != NULL;\r
1516 }\r
1517 \r
1518 void\r
1519 PopUpStartupDialog()\r
1520 {\r
1521     FARPROC lpProc;\r
1522     \r
1523     LoadLanguageFile(appData.language);\r
1524     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1525     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1526     FreeProcInstance(lpProc);\r
1527 }\r
1528 \r
1529 /*---------------------------------------------------------------------------*\\r
1530  *\r
1531  * GDI board drawing routines\r
1532  *\r
1533 \*---------------------------------------------------------------------------*/\r
1534 \r
1535 /* [AS] Draw square using background texture */\r
1536 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1537 {\r
1538     XFORM   x;\r
1539 \r
1540     if( mode == 0 ) {\r
1541         return; /* Should never happen! */\r
1542     }\r
1543 \r
1544     SetGraphicsMode( dst, GM_ADVANCED );\r
1545 \r
1546     switch( mode ) {\r
1547     case 1:\r
1548         /* Identity */\r
1549         break;\r
1550     case 2:\r
1551         /* X reflection */\r
1552         x.eM11 = -1.0;\r
1553         x.eM12 = 0;\r
1554         x.eM21 = 0;\r
1555         x.eM22 = 1.0;\r
1556         x.eDx = (FLOAT) dw + dx - 1;\r
1557         x.eDy = 0;\r
1558         dx = 0;\r
1559         SetWorldTransform( dst, &x );\r
1560         break;\r
1561     case 3:\r
1562         /* Y reflection */\r
1563         x.eM11 = 1.0;\r
1564         x.eM12 = 0;\r
1565         x.eM21 = 0;\r
1566         x.eM22 = -1.0;\r
1567         x.eDx = 0;\r
1568         x.eDy = (FLOAT) dh + dy - 1;\r
1569         dy = 0;\r
1570         SetWorldTransform( dst, &x );\r
1571         break;\r
1572     case 4:\r
1573         /* X/Y flip */\r
1574         x.eM11 = 0;\r
1575         x.eM12 = 1.0;\r
1576         x.eM21 = 1.0;\r
1577         x.eM22 = 0;\r
1578         x.eDx = (FLOAT) dx;\r
1579         x.eDy = (FLOAT) dy;\r
1580         dx = 0;\r
1581         dy = 0;\r
1582         SetWorldTransform( dst, &x );\r
1583         break;\r
1584     }\r
1585 \r
1586     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1587 \r
1588     x.eM11 = 1.0;\r
1589     x.eM12 = 0;\r
1590     x.eM21 = 0;\r
1591     x.eM22 = 1.0;\r
1592     x.eDx = 0;\r
1593     x.eDy = 0;\r
1594     SetWorldTransform( dst, &x );\r
1595 \r
1596     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1597 }\r
1598 \r
1599 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1600 enum {\r
1601     PM_WP = (int) WhitePawn, \r
1602     PM_WN = (int) WhiteKnight, \r
1603     PM_WB = (int) WhiteBishop, \r
1604     PM_WR = (int) WhiteRook, \r
1605     PM_WQ = (int) WhiteQueen, \r
1606     PM_WF = (int) WhiteFerz, \r
1607     PM_WW = (int) WhiteWazir, \r
1608     PM_WE = (int) WhiteAlfil, \r
1609     PM_WM = (int) WhiteMan, \r
1610     PM_WO = (int) WhiteCannon, \r
1611     PM_WU = (int) WhiteUnicorn, \r
1612     PM_WH = (int) WhiteNightrider, \r
1613     PM_WA = (int) WhiteAngel, \r
1614     PM_WC = (int) WhiteMarshall, \r
1615     PM_WAB = (int) WhiteCardinal, \r
1616     PM_WD = (int) WhiteDragon, \r
1617     PM_WL = (int) WhiteLance, \r
1618     PM_WS = (int) WhiteCobra, \r
1619     PM_WV = (int) WhiteFalcon, \r
1620     PM_WSG = (int) WhiteSilver, \r
1621     PM_WG = (int) WhiteGrasshopper, \r
1622     PM_WK = (int) WhiteKing,\r
1623     PM_BP = (int) BlackPawn, \r
1624     PM_BN = (int) BlackKnight, \r
1625     PM_BB = (int) BlackBishop, \r
1626     PM_BR = (int) BlackRook, \r
1627     PM_BQ = (int) BlackQueen, \r
1628     PM_BF = (int) BlackFerz, \r
1629     PM_BW = (int) BlackWazir, \r
1630     PM_BE = (int) BlackAlfil, \r
1631     PM_BM = (int) BlackMan,\r
1632     PM_BO = (int) BlackCannon, \r
1633     PM_BU = (int) BlackUnicorn, \r
1634     PM_BH = (int) BlackNightrider, \r
1635     PM_BA = (int) BlackAngel, \r
1636     PM_BC = (int) BlackMarshall, \r
1637     PM_BG = (int) BlackGrasshopper, \r
1638     PM_BAB = (int) BlackCardinal,\r
1639     PM_BD = (int) BlackDragon,\r
1640     PM_BL = (int) BlackLance,\r
1641     PM_BS = (int) BlackCobra,\r
1642     PM_BV = (int) BlackFalcon,\r
1643     PM_BSG = (int) BlackSilver,\r
1644     PM_BK = (int) BlackKing\r
1645 };\r
1646 \r
1647 static HFONT hPieceFont = NULL;\r
1648 static HBITMAP hPieceMask[(int) EmptySquare];\r
1649 static HBITMAP hPieceFace[(int) EmptySquare];\r
1650 static int fontBitmapSquareSize = 0;\r
1651 static char pieceToFontChar[(int) EmptySquare] =\r
1652                               { 'p', 'n', 'b', 'r', 'q', \r
1653                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1654                       'k', 'o', 'm', 'v', 't', 'w', \r
1655                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1656                                                               'l' };\r
1657 \r
1658 extern BOOL SetCharTable( char *table, const char * map );\r
1659 /* [HGM] moved to backend.c */\r
1660 \r
1661 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1662 {\r
1663     HBRUSH hbrush;\r
1664     BYTE r1 = GetRValue( color );\r
1665     BYTE g1 = GetGValue( color );\r
1666     BYTE b1 = GetBValue( color );\r
1667     BYTE r2 = r1 / 2;\r
1668     BYTE g2 = g1 / 2;\r
1669     BYTE b2 = b1 / 2;\r
1670     RECT rc;\r
1671 \r
1672     /* Create a uniform background first */\r
1673     hbrush = CreateSolidBrush( color );\r
1674     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1675     FillRect( hdc, &rc, hbrush );\r
1676     DeleteObject( hbrush );\r
1677     \r
1678     if( mode == 1 ) {\r
1679         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1680         int steps = squareSize / 2;\r
1681         int i;\r
1682 \r
1683         for( i=0; i<steps; i++ ) {\r
1684             BYTE r = r1 - (r1-r2) * i / steps;\r
1685             BYTE g = g1 - (g1-g2) * i / steps;\r
1686             BYTE b = b1 - (b1-b2) * i / steps;\r
1687 \r
1688             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1689             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1690             FillRect( hdc, &rc, hbrush );\r
1691             DeleteObject(hbrush);\r
1692         }\r
1693     }\r
1694     else if( mode == 2 ) {\r
1695         /* Diagonal gradient, good more or less for every piece */\r
1696         POINT triangle[3];\r
1697         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1698         HBRUSH hbrush_old;\r
1699         int steps = squareSize;\r
1700         int i;\r
1701 \r
1702         triangle[0].x = squareSize - steps;\r
1703         triangle[0].y = squareSize;\r
1704         triangle[1].x = squareSize;\r
1705         triangle[1].y = squareSize;\r
1706         triangle[2].x = squareSize;\r
1707         triangle[2].y = squareSize - steps;\r
1708 \r
1709         for( i=0; i<steps; i++ ) {\r
1710             BYTE r = r1 - (r1-r2) * i / steps;\r
1711             BYTE g = g1 - (g1-g2) * i / steps;\r
1712             BYTE b = b1 - (b1-b2) * i / steps;\r
1713 \r
1714             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1715             hbrush_old = SelectObject( hdc, hbrush );\r
1716             Polygon( hdc, triangle, 3 );\r
1717             SelectObject( hdc, hbrush_old );\r
1718             DeleteObject(hbrush);\r
1719             triangle[0].x++;\r
1720             triangle[2].y++;\r
1721         }\r
1722 \r
1723         SelectObject( hdc, hpen );\r
1724     }\r
1725 }\r
1726 \r
1727 /*\r
1728     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1729     seems to work ok. The main problem here is to find the "inside" of a chess\r
1730     piece: follow the steps as explained below.\r
1731 */\r
1732 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1733 {\r
1734     HBITMAP hbm;\r
1735     HBITMAP hbm_old;\r
1736     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1737     RECT rc;\r
1738     SIZE sz;\r
1739     POINT pt;\r
1740     int backColor = whitePieceColor; \r
1741     int foreColor = blackPieceColor;\r
1742     \r
1743     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1744         backColor = appData.fontBackColorWhite;\r
1745         foreColor = appData.fontForeColorWhite;\r
1746     }\r
1747     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1748         backColor = appData.fontBackColorBlack;\r
1749         foreColor = appData.fontForeColorBlack;\r
1750     }\r
1751 \r
1752     /* Mask */\r
1753     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1754 \r
1755     hbm_old = SelectObject( hdc, hbm );\r
1756 \r
1757     rc.left = 0;\r
1758     rc.top = 0;\r
1759     rc.right = squareSize;\r
1760     rc.bottom = squareSize;\r
1761 \r
1762     /* Step 1: background is now black */\r
1763     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1764 \r
1765     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1766 \r
1767     pt.x = (squareSize - sz.cx) / 2;\r
1768     pt.y = (squareSize - sz.cy) / 2;\r
1769 \r
1770     SetBkMode( hdc, TRANSPARENT );\r
1771     SetTextColor( hdc, chroma );\r
1772     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1773     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1774 \r
1775     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1776     /* Step 3: the area outside the piece is filled with white */\r
1777 //    FloodFill( hdc, 0, 0, chroma );\r
1778     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1779     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1780     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1781     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1782     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1783     /* \r
1784         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1785         but if the start point is not inside the piece we're lost!\r
1786         There should be a better way to do this... if we could create a region or path\r
1787         from the fill operation we would be fine for example.\r
1788     */\r
1789 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1790     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1791 \r
1792     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1793         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1794         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1795 \r
1796         SelectObject( dc2, bm2 );\r
1797         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1798         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1799         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1800         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1801         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1802 \r
1803         DeleteDC( dc2 );\r
1804         DeleteObject( bm2 );\r
1805     }\r
1806 \r
1807     SetTextColor( hdc, 0 );\r
1808     /* \r
1809         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1810         draw the piece again in black for safety.\r
1811     */\r
1812     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1813 \r
1814     SelectObject( hdc, hbm_old );\r
1815 \r
1816     if( hPieceMask[index] != NULL ) {\r
1817         DeleteObject( hPieceMask[index] );\r
1818     }\r
1819 \r
1820     hPieceMask[index] = hbm;\r
1821 \r
1822     /* Face */\r
1823     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1824 \r
1825     SelectObject( hdc, hbm );\r
1826 \r
1827     {\r
1828         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1829         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1830         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1831 \r
1832         SelectObject( dc1, hPieceMask[index] );\r
1833         SelectObject( dc2, bm2 );\r
1834         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1835         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1836         \r
1837         /* \r
1838             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1839             the piece background and deletes (makes transparent) the rest.\r
1840             Thanks to that mask, we are free to paint the background with the greates\r
1841             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1842             We use this, to make gradients and give the pieces a "roundish" look.\r
1843         */\r
1844         SetPieceBackground( hdc, backColor, 2 );\r
1845         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1846 \r
1847         DeleteDC( dc2 );\r
1848         DeleteDC( dc1 );\r
1849         DeleteObject( bm2 );\r
1850     }\r
1851 \r
1852     SetTextColor( hdc, foreColor );\r
1853     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1854 \r
1855     SelectObject( hdc, hbm_old );\r
1856 \r
1857     if( hPieceFace[index] != NULL ) {\r
1858         DeleteObject( hPieceFace[index] );\r
1859     }\r
1860 \r
1861     hPieceFace[index] = hbm;\r
1862 }\r
1863 \r
1864 static int TranslatePieceToFontPiece( int piece )\r
1865 {\r
1866     switch( piece ) {\r
1867     case BlackPawn:\r
1868         return PM_BP;\r
1869     case BlackKnight:\r
1870         return PM_BN;\r
1871     case BlackBishop:\r
1872         return PM_BB;\r
1873     case BlackRook:\r
1874         return PM_BR;\r
1875     case BlackQueen:\r
1876         return PM_BQ;\r
1877     case BlackKing:\r
1878         return PM_BK;\r
1879     case WhitePawn:\r
1880         return PM_WP;\r
1881     case WhiteKnight:\r
1882         return PM_WN;\r
1883     case WhiteBishop:\r
1884         return PM_WB;\r
1885     case WhiteRook:\r
1886         return PM_WR;\r
1887     case WhiteQueen:\r
1888         return PM_WQ;\r
1889     case WhiteKing:\r
1890         return PM_WK;\r
1891 \r
1892     case BlackAngel:\r
1893         return PM_BA;\r
1894     case BlackMarshall:\r
1895         return PM_BC;\r
1896     case BlackFerz:\r
1897         return PM_BF;\r
1898     case BlackNightrider:\r
1899         return PM_BH;\r
1900     case BlackAlfil:\r
1901         return PM_BE;\r
1902     case BlackWazir:\r
1903         return PM_BW;\r
1904     case BlackUnicorn:\r
1905         return PM_BU;\r
1906     case BlackCannon:\r
1907         return PM_BO;\r
1908     case BlackGrasshopper:\r
1909         return PM_BG;\r
1910     case BlackMan:\r
1911         return PM_BM;\r
1912     case BlackSilver:\r
1913         return PM_BSG;\r
1914     case BlackLance:\r
1915         return PM_BL;\r
1916     case BlackFalcon:\r
1917         return PM_BV;\r
1918     case BlackCobra:\r
1919         return PM_BS;\r
1920     case BlackCardinal:\r
1921         return PM_BAB;\r
1922     case BlackDragon:\r
1923         return PM_BD;\r
1924 \r
1925     case WhiteAngel:\r
1926         return PM_WA;\r
1927     case WhiteMarshall:\r
1928         return PM_WC;\r
1929     case WhiteFerz:\r
1930         return PM_WF;\r
1931     case WhiteNightrider:\r
1932         return PM_WH;\r
1933     case WhiteAlfil:\r
1934         return PM_WE;\r
1935     case WhiteWazir:\r
1936         return PM_WW;\r
1937     case WhiteUnicorn:\r
1938         return PM_WU;\r
1939     case WhiteCannon:\r
1940         return PM_WO;\r
1941     case WhiteGrasshopper:\r
1942         return PM_WG;\r
1943     case WhiteMan:\r
1944         return PM_WM;\r
1945     case WhiteSilver:\r
1946         return PM_WSG;\r
1947     case WhiteLance:\r
1948         return PM_WL;\r
1949     case WhiteFalcon:\r
1950         return PM_WV;\r
1951     case WhiteCobra:\r
1952         return PM_WS;\r
1953     case WhiteCardinal:\r
1954         return PM_WAB;\r
1955     case WhiteDragon:\r
1956         return PM_WD;\r
1957     }\r
1958 \r
1959     return 0;\r
1960 }\r
1961 \r
1962 void CreatePiecesFromFont()\r
1963 {\r
1964     LOGFONT lf;\r
1965     HDC hdc_window = NULL;\r
1966     HDC hdc = NULL;\r
1967     HFONT hfont_old;\r
1968     int fontHeight;\r
1969     int i;\r
1970 \r
1971     if( fontBitmapSquareSize < 0 ) {\r
1972         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1973         return;\r
1974     }\r
1975 \r
1976     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1977         fontBitmapSquareSize = -1;\r
1978         return;\r
1979     }\r
1980 \r
1981     if( fontBitmapSquareSize != squareSize ) {\r
1982         hdc_window = GetDC( hwndMain );\r
1983         hdc = CreateCompatibleDC( hdc_window );\r
1984 \r
1985         if( hPieceFont != NULL ) {\r
1986             DeleteObject( hPieceFont );\r
1987         }\r
1988         else {\r
1989             for( i=0; i<=(int)BlackKing; i++ ) {\r
1990                 hPieceMask[i] = NULL;\r
1991                 hPieceFace[i] = NULL;\r
1992             }\r
1993         }\r
1994 \r
1995         fontHeight = 75;\r
1996 \r
1997         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1998             fontHeight = appData.fontPieceSize;\r
1999         }\r
2000 \r
2001         fontHeight = (fontHeight * squareSize) / 100;\r
2002 \r
2003         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2004         lf.lfWidth = 0;\r
2005         lf.lfEscapement = 0;\r
2006         lf.lfOrientation = 0;\r
2007         lf.lfWeight = FW_NORMAL;\r
2008         lf.lfItalic = 0;\r
2009         lf.lfUnderline = 0;\r
2010         lf.lfStrikeOut = 0;\r
2011         lf.lfCharSet = DEFAULT_CHARSET;\r
2012         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2013         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2014         lf.lfQuality = PROOF_QUALITY;\r
2015         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2016         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2017         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2018 \r
2019         hPieceFont = CreateFontIndirect( &lf );\r
2020 \r
2021         if( hPieceFont == NULL ) {\r
2022             fontBitmapSquareSize = -2;\r
2023         }\r
2024         else {\r
2025             /* Setup font-to-piece character table */\r
2026             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2027                 /* No (or wrong) global settings, try to detect the font */\r
2028                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2029                     /* Alpha */\r
2030                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2031                 }\r
2032                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2033                     /* DiagramTT* family */\r
2034                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2035                 }\r
2036                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2037                     /* Fairy symbols */\r
2038                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2039                 }\r
2040                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2041                     /* Good Companion (Some characters get warped as literal :-( */\r
2042                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2043                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2044                     SetCharTable(pieceToFontChar, s);\r
2045                 }\r
2046                 else {\r
2047                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2048                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2049                 }\r
2050             }\r
2051 \r
2052             /* Create bitmaps */\r
2053             hfont_old = SelectObject( hdc, hPieceFont );\r
2054             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2055                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2056                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2057 \r
2058             SelectObject( hdc, hfont_old );\r
2059 \r
2060             fontBitmapSquareSize = squareSize;\r
2061         }\r
2062     }\r
2063 \r
2064     if( hdc != NULL ) {\r
2065         DeleteDC( hdc );\r
2066     }\r
2067 \r
2068     if( hdc_window != NULL ) {\r
2069         ReleaseDC( hwndMain, hdc_window );\r
2070     }\r
2071 }\r
2072 \r
2073 HBITMAP\r
2074 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2075 {\r
2076   char name[128];\r
2077 \r
2078     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2079   if (gameInfo.event &&\r
2080       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2081       strcmp(name, "k80s") == 0) {\r
2082     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2083   }\r
2084   return LoadBitmap(hinst, name);\r
2085 }\r
2086 \r
2087 \r
2088 /* Insert a color into the program's logical palette\r
2089    structure.  This code assumes the given color is\r
2090    the result of the RGB or PALETTERGB macro, and it\r
2091    knows how those macros work (which is documented).\r
2092 */\r
2093 VOID\r
2094 InsertInPalette(COLORREF color)\r
2095 {\r
2096   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2097 \r
2098   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2099     DisplayFatalError(_("Too many colors"), 0, 1);\r
2100     pLogPal->palNumEntries--;\r
2101     return;\r
2102   }\r
2103 \r
2104   pe->peFlags = (char) 0;\r
2105   pe->peRed = (char) (0xFF & color);\r
2106   pe->peGreen = (char) (0xFF & (color >> 8));\r
2107   pe->peBlue = (char) (0xFF & (color >> 16));\r
2108   return;\r
2109 }\r
2110 \r
2111 \r
2112 VOID\r
2113 InitDrawingColors()\r
2114 {\r
2115   if (pLogPal == NULL) {\r
2116     /* Allocate enough memory for a logical palette with\r
2117      * PALETTESIZE entries and set the size and version fields\r
2118      * of the logical palette structure.\r
2119      */\r
2120     pLogPal = (NPLOGPALETTE)\r
2121       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2122                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2123     pLogPal->palVersion    = 0x300;\r
2124   }\r
2125   pLogPal->palNumEntries = 0;\r
2126 \r
2127   InsertInPalette(lightSquareColor);\r
2128   InsertInPalette(darkSquareColor);\r
2129   InsertInPalette(whitePieceColor);\r
2130   InsertInPalette(blackPieceColor);\r
2131   InsertInPalette(highlightSquareColor);\r
2132   InsertInPalette(premoveHighlightColor);\r
2133 \r
2134   /*  create a logical color palette according the information\r
2135    *  in the LOGPALETTE structure.\r
2136    */\r
2137   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2138 \r
2139   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2140   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2141   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2142   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2143   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2144   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2145   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2146   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2147   /* [AS] Force rendering of the font-based pieces */\r
2148   if( fontBitmapSquareSize > 0 ) {\r
2149     fontBitmapSquareSize = 0;\r
2150   }\r
2151 }\r
2152 \r
2153 \r
2154 int\r
2155 BoardWidth(int boardSize, int n)\r
2156 { /* [HGM] argument n added to allow different width and height */\r
2157   int lineGap = sizeInfo[boardSize].lineGap;\r
2158 \r
2159   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2160       lineGap = appData.overrideLineGap;\r
2161   }\r
2162 \r
2163   return (n + 1) * lineGap +\r
2164           n * sizeInfo[boardSize].squareSize;\r
2165 }\r
2166 \r
2167 /* Respond to board resize by dragging edge */\r
2168 VOID\r
2169 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2170 {\r
2171   BoardSize newSize = NUM_SIZES - 1;\r
2172   static int recurse = 0;\r
2173   if (IsIconic(hwndMain)) return;\r
2174   if (recurse > 0) return;\r
2175   recurse++;\r
2176   while (newSize > 0) {\r
2177         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2178         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2179            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2180     newSize--;\r
2181   } \r
2182   boardSize = newSize;\r
2183   InitDrawingSizes(boardSize, flags);\r
2184   recurse--;\r
2185 }\r
2186 \r
2187 \r
2188 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2189 \r
2190 VOID\r
2191 InitDrawingSizes(BoardSize boardSize, int flags)\r
2192 {\r
2193   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2194   ChessSquare piece;\r
2195   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2196   HDC hdc;\r
2197   SIZE clockSize, messageSize;\r
2198   HFONT oldFont;\r
2199   char buf[MSG_SIZ];\r
2200   char *str;\r
2201   HMENU hmenu = GetMenu(hwndMain);\r
2202   RECT crect, wrect, oldRect;\r
2203   int offby;\r
2204   LOGBRUSH logbrush;\r
2205 \r
2206   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2207   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2208 \r
2209   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2210   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2211 \r
2212   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2213   oldRect.top = wpMain.y;\r
2214   oldRect.right = wpMain.x + wpMain.width;\r
2215   oldRect.bottom = wpMain.y + wpMain.height;\r
2216 \r
2217   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2218   smallLayout = sizeInfo[boardSize].smallLayout;\r
2219   squareSize = sizeInfo[boardSize].squareSize;\r
2220   lineGap = sizeInfo[boardSize].lineGap;\r
2221   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2222 \r
2223   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2224       lineGap = appData.overrideLineGap;\r
2225   }\r
2226 \r
2227   if (tinyLayout != oldTinyLayout) {\r
2228     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2229     if (tinyLayout) {\r
2230       style &= ~WS_SYSMENU;\r
2231       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2232                  "&Minimize\tCtrl+F4");\r
2233     } else {\r
2234       style |= WS_SYSMENU;\r
2235       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2236     }\r
2237     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2238 \r
2239     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2240       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2241         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2242     }\r
2243     DrawMenuBar(hwndMain);\r
2244   }\r
2245 \r
2246   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2247   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2248 \r
2249   /* Get text area sizes */\r
2250   hdc = GetDC(hwndMain);\r
2251   if (appData.clockMode) {\r
2252     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2253   } else {\r
2254     snprintf(buf, MSG_SIZ, _("White"));\r
2255   }\r
2256   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2257   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2258   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2259   str = _("We only care about the height here");\r
2260   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2261   SelectObject(hdc, oldFont);\r
2262   ReleaseDC(hwndMain, hdc);\r
2263 \r
2264   /* Compute where everything goes */\r
2265   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2266         /* [HGM] logo: if either logo is on, reserve space for it */\r
2267         logoHeight =  2*clockSize.cy;\r
2268         leftLogoRect.left   = OUTER_MARGIN;\r
2269         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2270         leftLogoRect.top    = OUTER_MARGIN;\r
2271         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2272 \r
2273         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2274         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2275         rightLogoRect.top    = OUTER_MARGIN;\r
2276         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2277 \r
2278 \r
2279     whiteRect.left = leftLogoRect.right;\r
2280     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2281     whiteRect.top = OUTER_MARGIN;\r
2282     whiteRect.bottom = whiteRect.top + logoHeight;\r
2283 \r
2284     blackRect.right = rightLogoRect.left;\r
2285     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2286     blackRect.top = whiteRect.top;\r
2287     blackRect.bottom = whiteRect.bottom;\r
2288   } else {\r
2289     whiteRect.left = OUTER_MARGIN;\r
2290     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2291     whiteRect.top = OUTER_MARGIN;\r
2292     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2293 \r
2294     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2295     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2296     blackRect.top = whiteRect.top;\r
2297     blackRect.bottom = whiteRect.bottom;\r
2298 \r
2299     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2300   }\r
2301 \r
2302   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2303   if (appData.showButtonBar) {\r
2304     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2305       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2306   } else {\r
2307     messageRect.right = OUTER_MARGIN + boardWidth;\r
2308   }\r
2309   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2310   messageRect.bottom = messageRect.top + messageSize.cy;\r
2311 \r
2312   boardRect.left = OUTER_MARGIN;\r
2313   boardRect.right = boardRect.left + boardWidth;\r
2314   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2315   boardRect.bottom = boardRect.top + boardHeight;\r
2316 \r
2317   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2318   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2319   oldBoardSize = boardSize;\r
2320   oldTinyLayout = tinyLayout;\r
2321   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2322   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2323     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2324   winW *= 1 + twoBoards;\r
2325   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2326   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2327   wpMain.height = winH; //       without disturbing window attachments\r
2328   GetWindowRect(hwndMain, &wrect);\r
2329   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2330                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2331 \r
2332   // [HGM] placement: let attached windows follow size change.\r
2333   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2334   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2335   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2336   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2337   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2338 \r
2339   /* compensate if menu bar wrapped */\r
2340   GetClientRect(hwndMain, &crect);\r
2341   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2342   wpMain.height += offby;\r
2343   switch (flags) {\r
2344   case WMSZ_TOPLEFT:\r
2345     SetWindowPos(hwndMain, NULL, \r
2346                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2347                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2348     break;\r
2349 \r
2350   case WMSZ_TOPRIGHT:\r
2351   case WMSZ_TOP:\r
2352     SetWindowPos(hwndMain, NULL, \r
2353                  wrect.left, wrect.bottom - wpMain.height, \r
2354                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2355     break;\r
2356 \r
2357   case WMSZ_BOTTOMLEFT:\r
2358   case WMSZ_LEFT:\r
2359     SetWindowPos(hwndMain, NULL, \r
2360                  wrect.right - wpMain.width, wrect.top, \r
2361                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2362     break;\r
2363 \r
2364   case WMSZ_BOTTOMRIGHT:\r
2365   case WMSZ_BOTTOM:\r
2366   case WMSZ_RIGHT:\r
2367   default:\r
2368     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2369                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2370     break;\r
2371   }\r
2372 \r
2373   hwndPause = NULL;\r
2374   for (i = 0; i < N_BUTTONS; i++) {\r
2375     if (buttonDesc[i].hwnd != NULL) {\r
2376       DestroyWindow(buttonDesc[i].hwnd);\r
2377       buttonDesc[i].hwnd = NULL;\r
2378     }\r
2379     if (appData.showButtonBar) {\r
2380       buttonDesc[i].hwnd =\r
2381         CreateWindow("BUTTON", buttonDesc[i].label,\r
2382                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2383                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2384                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2385                      (HMENU) buttonDesc[i].id,\r
2386                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2387       if (tinyLayout) {\r
2388         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2389                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2390                     MAKELPARAM(FALSE, 0));\r
2391       }\r
2392       if (buttonDesc[i].id == IDM_Pause)\r
2393         hwndPause = buttonDesc[i].hwnd;\r
2394       buttonDesc[i].wndproc = (WNDPROC)\r
2395         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2396     }\r
2397   }\r
2398   if (gridPen != NULL) DeleteObject(gridPen);\r
2399   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2400   if (premovePen != NULL) DeleteObject(premovePen);\r
2401   if (lineGap != 0) {\r
2402     logbrush.lbStyle = BS_SOLID;\r
2403     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2404     gridPen =\r
2405       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2406                    lineGap, &logbrush, 0, NULL);\r
2407     logbrush.lbColor = highlightSquareColor;\r
2408     highlightPen =\r
2409       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2410                    lineGap, &logbrush, 0, NULL);\r
2411 \r
2412     logbrush.lbColor = premoveHighlightColor; \r
2413     premovePen =\r
2414       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2415                    lineGap, &logbrush, 0, NULL);\r
2416 \r
2417     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2418     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2419       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2420       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2421         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2422       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2423         BOARD_WIDTH * (squareSize + lineGap);\r
2424       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2425     }\r
2426     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2427       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2428       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2429         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2430         lineGap / 2 + (i * (squareSize + lineGap));\r
2431       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2432         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2433       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2434     }\r
2435   }\r
2436 \r
2437   /* [HGM] Licensing requirement */\r
2438 #ifdef GOTHIC\r
2439   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2440 #endif\r
2441 #ifdef FALCON\r
2442   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2443 #endif\r
2444   GothicPopUp( "", VariantNormal);\r
2445 \r
2446 \r
2447 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2448 \r
2449   /* Load piece bitmaps for this board size */\r
2450   for (i=0; i<=2; i++) {\r
2451     for (piece = WhitePawn;\r
2452          (int) piece < (int) BlackPawn;\r
2453          piece = (ChessSquare) ((int) piece + 1)) {\r
2454       if (pieceBitmap[i][piece] != NULL)\r
2455         DeleteObject(pieceBitmap[i][piece]);\r
2456     }\r
2457   }\r
2458 \r
2459   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2460   // Orthodox Chess pieces\r
2461   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2462   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2463   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2464   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2465   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2466   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2467   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2468   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2469   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2470   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2471   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2472   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2473   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2474   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2475   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2476   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2477     // in Shogi, Hijack the unused Queen for Lance\r
2478     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2479     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2480     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2481   } else {\r
2482     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2483     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2484     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2485   }\r
2486 \r
2487   if(squareSize <= 72 && squareSize >= 33) { \r
2488     /* A & C are available in most sizes now */\r
2489     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2490       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2491       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2492       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2493       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2494       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2495       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2496       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2497       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2498       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2499       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2500       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2501       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2502     } else { // Smirf-like\r
2503       if(gameInfo.variant == VariantSChess) {\r
2504         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2505         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2506         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2507       } else {\r
2508         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2509         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2510         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2511       }\r
2512     }\r
2513     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2514       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2515       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2516       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2517     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2518       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2519       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2520       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2521     } else { // WinBoard standard\r
2522       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2523       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2524       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2525     }\r
2526   }\r
2527 \r
2528 \r
2529   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2530     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2531     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2532     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2533     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2534     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2535     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2536     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2537     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2538     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2539     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2540     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2541     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2542     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2543     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2544     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2545     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2546     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2547     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2548     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2549     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2550     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2551     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2552     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2553     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2554     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2555     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2556     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2557     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2558     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2559     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2560 \r
2561     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2562       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2563       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2564       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2565       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2566       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2567       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2568       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2569       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2570       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2571       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2572       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2573       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2574     } else {\r
2575       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2576       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2577       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2578       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2579       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2580       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2581       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2582       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2583       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2584       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2585       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2586       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2587     }\r
2588 \r
2589   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2590     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2591     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2592     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2593     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2594     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2595     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2596     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2597     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2598     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2599     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2600     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2601     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2602     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2603     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2604   }\r
2605 \r
2606 \r
2607   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2608   /* special Shogi support in this size */\r
2609   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2610       for (piece = WhitePawn;\r
2611            (int) piece < (int) BlackPawn;\r
2612            piece = (ChessSquare) ((int) piece + 1)) {\r
2613         if (pieceBitmap[i][piece] != NULL)\r
2614           DeleteObject(pieceBitmap[i][piece]);\r
2615       }\r
2616     }\r
2617   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2618   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2619   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2620   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2621   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2622   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2623   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2624   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2625   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2626   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2627   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2628   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2629   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2630   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2631   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2632   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2633   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2634   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2635   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2636   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2637   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2638   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2639   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2640   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2641   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2642   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2643   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2644   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2645   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2646   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2647   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2648   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2649   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2650   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2651   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2652   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2653   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2654   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2655   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2656   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2657   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2658   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2659   minorSize = 0;\r
2660   }\r
2661 }\r
2662 \r
2663 HBITMAP\r
2664 PieceBitmap(ChessSquare p, int kind)\r
2665 {\r
2666   if ((int) p >= (int) BlackPawn)\r
2667     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2668 \r
2669   return pieceBitmap[kind][(int) p];\r
2670 }\r
2671 \r
2672 /***************************************************************/\r
2673 \r
2674 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2675 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2676 /*\r
2677 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2678 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2679 */\r
2680 \r
2681 VOID\r
2682 SquareToPos(int row, int column, int * x, int * y)\r
2683 {\r
2684   if (flipView) {\r
2685     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2686     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2687   } else {\r
2688     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2689     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2690   }\r
2691 }\r
2692 \r
2693 VOID\r
2694 DrawCoordsOnDC(HDC hdc)\r
2695 {\r
2696   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
2697   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
2698   char str[2] = { NULLCHAR, NULLCHAR };\r
2699   int oldMode, oldAlign, x, y, start, i;\r
2700   HFONT oldFont;\r
2701   HBRUSH oldBrush;\r
2702 \r
2703   if (!appData.showCoords)\r
2704     return;\r
2705 \r
2706   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2707 \r
2708   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2709   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2710   oldAlign = GetTextAlign(hdc);\r
2711   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2712 \r
2713   y = boardRect.top + lineGap;\r
2714   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2715 \r
2716   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2717   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2718     str[0] = files[start + i];\r
2719     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2720     y += squareSize + lineGap;\r
2721   }\r
2722 \r
2723   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2724 \r
2725   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2726   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2727     str[0] = ranks[start + i];\r
2728     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2729     x += squareSize + lineGap;\r
2730   }    \r
2731 \r
2732   SelectObject(hdc, oldBrush);\r
2733   SetBkMode(hdc, oldMode);\r
2734   SetTextAlign(hdc, oldAlign);\r
2735   SelectObject(hdc, oldFont);\r
2736 }\r
2737 \r
2738 VOID\r
2739 DrawGridOnDC(HDC hdc)\r
2740 {\r
2741   HPEN oldPen;\r
2742  \r
2743   if (lineGap != 0) {\r
2744     oldPen = SelectObject(hdc, gridPen);\r
2745     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2746     SelectObject(hdc, oldPen);\r
2747   }\r
2748 }\r
2749 \r
2750 #define HIGHLIGHT_PEN 0\r
2751 #define PREMOVE_PEN   1\r
2752 \r
2753 VOID\r
2754 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2755 {\r
2756   int x1, y1;\r
2757   HPEN oldPen, hPen;\r
2758   if (lineGap == 0) return;\r
2759   if (flipView) {\r
2760     x1 = boardRect.left +\r
2761       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2762     y1 = boardRect.top +\r
2763       lineGap/2 + y * (squareSize + lineGap);\r
2764   } else {\r
2765     x1 = boardRect.left +\r
2766       lineGap/2 + x * (squareSize + lineGap);\r
2767     y1 = boardRect.top +\r
2768       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2769   }\r
2770   hPen = pen ? premovePen : highlightPen;\r
2771   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2772   MoveToEx(hdc, x1, y1, NULL);\r
2773   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2774   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2775   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2776   LineTo(hdc, x1, y1);\r
2777   SelectObject(hdc, oldPen);\r
2778 }\r
2779 \r
2780 VOID\r
2781 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2782 {\r
2783   int i;\r
2784   for (i=0; i<2; i++) {\r
2785     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2786       DrawHighlightOnDC(hdc, TRUE,\r
2787                         h->sq[i].x, h->sq[i].y,\r
2788                         pen);\r
2789   }\r
2790 }\r
2791 \r
2792 /* Note: sqcolor is used only in monoMode */\r
2793 /* Note that this code is largely duplicated in woptions.c,\r
2794    function DrawSampleSquare, so that needs to be updated too */\r
2795 VOID\r
2796 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2797 {\r
2798   HBITMAP oldBitmap;\r
2799   HBRUSH oldBrush;\r
2800   int tmpSize;\r
2801 \r
2802   if (appData.blindfold) return;\r
2803 \r
2804   /* [AS] Use font-based pieces if needed */\r
2805   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2806     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2807     CreatePiecesFromFont();\r
2808 \r
2809     if( fontBitmapSquareSize == squareSize ) {\r
2810         int index = TranslatePieceToFontPiece(piece);\r
2811 \r
2812         SelectObject( tmphdc, hPieceMask[ index ] );\r
2813 \r
2814       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2815         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2816       else\r
2817         BitBlt( hdc,\r
2818             x, y,\r
2819             squareSize, squareSize,\r
2820             tmphdc,\r
2821             0, 0,\r
2822             SRCAND );\r
2823 \r
2824         SelectObject( tmphdc, hPieceFace[ index ] );\r
2825 \r
2826       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2827         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2828       else\r
2829         BitBlt( hdc,\r
2830             x, y,\r
2831             squareSize, squareSize,\r
2832             tmphdc,\r
2833             0, 0,\r
2834             SRCPAINT );\r
2835 \r
2836         return;\r
2837     }\r
2838   }\r
2839 \r
2840   if (appData.monoMode) {\r
2841     SelectObject(tmphdc, PieceBitmap(piece, \r
2842       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2843     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2844            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2845   } else {\r
2846     tmpSize = squareSize;\r
2847     if(minorSize &&\r
2848         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2849          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2850       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2851       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2852       x += (squareSize - minorSize)>>1;\r
2853       y += squareSize - minorSize - 2;\r
2854       tmpSize = minorSize;\r
2855     }\r
2856     if (color || appData.allWhite ) {\r
2857       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2858       if( color )\r
2859               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2860       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2861       if(appData.upsideDown && color==flipView)\r
2862         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2863       else\r
2864         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2865       /* Use black for outline of white pieces */\r
2866       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2867       if(appData.upsideDown && color==flipView)\r
2868         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2869       else\r
2870         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2871     } else {\r
2872       /* Use square color for details of black pieces */\r
2873       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2874       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2875       if(appData.upsideDown && !flipView)\r
2876         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2877       else\r
2878         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2879     }\r
2880     SelectObject(hdc, oldBrush);\r
2881     SelectObject(tmphdc, oldBitmap);\r
2882   }\r
2883 }\r
2884 \r
2885 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2886 int GetBackTextureMode( int algo )\r
2887 {\r
2888     int result = BACK_TEXTURE_MODE_DISABLED;\r
2889 \r
2890     switch( algo ) \r
2891     {\r
2892         case BACK_TEXTURE_MODE_PLAIN:\r
2893             result = 1; /* Always use identity map */\r
2894             break;\r
2895         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2896             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2897             break;\r
2898     }\r
2899 \r
2900     return result;\r
2901 }\r
2902 \r
2903 /* \r
2904     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2905     to handle redraws cleanly (as random numbers would always be different).\r
2906 */\r
2907 VOID RebuildTextureSquareInfo()\r
2908 {\r
2909     BITMAP bi;\r
2910     int lite_w = 0;\r
2911     int lite_h = 0;\r
2912     int dark_w = 0;\r
2913     int dark_h = 0;\r
2914     int row;\r
2915     int col;\r
2916 \r
2917     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2918 \r
2919     if( liteBackTexture != NULL ) {\r
2920         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2921             lite_w = bi.bmWidth;\r
2922             lite_h = bi.bmHeight;\r
2923         }\r
2924     }\r
2925 \r
2926     if( darkBackTexture != NULL ) {\r
2927         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2928             dark_w = bi.bmWidth;\r
2929             dark_h = bi.bmHeight;\r
2930         }\r
2931     }\r
2932 \r
2933     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2934         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2935             if( (col + row) & 1 ) {\r
2936                 /* Lite square */\r
2937                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2938                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2939                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2940                   else\r
2941                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2942                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2943                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2944                   else\r
2945                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2946                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2947                 }\r
2948             }\r
2949             else {\r
2950                 /* Dark square */\r
2951                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2952                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2953                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2954                   else\r
2955                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2956                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2957                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2958                   else\r
2959                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2960                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2961                 }\r
2962             }\r
2963         }\r
2964     }\r
2965 }\r
2966 \r
2967 /* [AS] Arrow highlighting support */\r
2968 \r
2969 static double A_WIDTH = 5; /* Width of arrow body */\r
2970 \r
2971 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2972 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2973 \r
2974 static double Sqr( double x )\r
2975 {\r
2976     return x*x;\r
2977 }\r
2978 \r
2979 static int Round( double x )\r
2980 {\r
2981     return (int) (x + 0.5);\r
2982 }\r
2983 \r
2984 /* Draw an arrow between two points using current settings */\r
2985 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2986 {\r
2987     POINT arrow[7];\r
2988     double dx, dy, j, k, x, y;\r
2989 \r
2990     if( d_x == s_x ) {\r
2991         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2992 \r
2993         arrow[0].x = s_x + A_WIDTH + 0.5;\r
2994         arrow[0].y = s_y;\r
2995 \r
2996         arrow[1].x = s_x + A_WIDTH + 0.5;\r
2997         arrow[1].y = d_y - h;\r
2998 \r
2999         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3000         arrow[2].y = d_y - h;\r
3001 \r
3002         arrow[3].x = d_x;\r
3003         arrow[3].y = d_y;\r
3004 \r
3005         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3006         arrow[5].y = d_y - h;\r
3007 \r
3008         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3009         arrow[4].y = d_y - h;\r
3010 \r
3011         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3012         arrow[6].y = s_y;\r
3013     }\r
3014     else if( d_y == s_y ) {\r
3015         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3016 \r
3017         arrow[0].x = s_x;\r
3018         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3019 \r
3020         arrow[1].x = d_x - w;\r
3021         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3022 \r
3023         arrow[2].x = d_x - w;\r
3024         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3025 \r
3026         arrow[3].x = d_x;\r
3027         arrow[3].y = d_y;\r
3028 \r
3029         arrow[5].x = d_x - w;\r
3030         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3031 \r
3032         arrow[4].x = d_x - w;\r
3033         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3034 \r
3035         arrow[6].x = s_x;\r
3036         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3037     }\r
3038     else {\r
3039         /* [AS] Needed a lot of paper for this! :-) */\r
3040         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3041         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3042   \r
3043         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3044 \r
3045         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3046 \r
3047         x = s_x;\r
3048         y = s_y;\r
3049 \r
3050         arrow[0].x = Round(x - j);\r
3051         arrow[0].y = Round(y + j*dx);\r
3052 \r
3053         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3054         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3055 \r
3056         if( d_x > s_x ) {\r
3057             x = (double) d_x - k;\r
3058             y = (double) d_y - k*dy;\r
3059         }\r
3060         else {\r
3061             x = (double) d_x + k;\r
3062             y = (double) d_y + k*dy;\r
3063         }\r
3064 \r
3065         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3066 \r
3067         arrow[6].x = Round(x - j);\r
3068         arrow[6].y = Round(y + j*dx);\r
3069 \r
3070         arrow[2].x = Round(arrow[6].x + 2*j);\r
3071         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3072 \r
3073         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3074         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3075 \r
3076         arrow[4].x = d_x;\r
3077         arrow[4].y = d_y;\r
3078 \r
3079         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3080         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3081     }\r
3082 \r
3083     Polygon( hdc, arrow, 7 );\r
3084 }\r
3085 \r
3086 /* [AS] Draw an arrow between two squares */\r
3087 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3088 {\r
3089     int s_x, s_y, d_x, d_y;\r
3090     HPEN hpen;\r
3091     HPEN holdpen;\r
3092     HBRUSH hbrush;\r
3093     HBRUSH holdbrush;\r
3094     LOGBRUSH stLB;\r
3095 \r
3096     if( s_col == d_col && s_row == d_row ) {\r
3097         return;\r
3098     }\r
3099 \r
3100     /* Get source and destination points */\r
3101     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3102     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3103 \r
3104     if( d_y > s_y ) {\r
3105         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3106     }\r
3107     else if( d_y < s_y ) {\r
3108         d_y += squareSize / 2 + squareSize / 4;\r
3109     }\r
3110     else {\r
3111         d_y += squareSize / 2;\r
3112     }\r
3113 \r
3114     if( d_x > s_x ) {\r
3115         d_x += squareSize / 2 - squareSize / 4;\r
3116     }\r
3117     else if( d_x < s_x ) {\r
3118         d_x += squareSize / 2 + squareSize / 4;\r
3119     }\r
3120     else {\r
3121         d_x += squareSize / 2;\r
3122     }\r
3123 \r
3124     s_x += squareSize / 2;\r
3125     s_y += squareSize / 2;\r
3126 \r
3127     /* Adjust width */\r
3128     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3129 \r
3130     /* Draw */\r
3131     stLB.lbStyle = BS_SOLID;\r
3132     stLB.lbColor = appData.highlightArrowColor;\r
3133     stLB.lbHatch = 0;\r
3134 \r
3135     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3136     holdpen = SelectObject( hdc, hpen );\r
3137     hbrush = CreateBrushIndirect( &stLB );\r
3138     holdbrush = SelectObject( hdc, hbrush );\r
3139 \r
3140     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3141 \r
3142     SelectObject( hdc, holdpen );\r
3143     SelectObject( hdc, holdbrush );\r
3144     DeleteObject( hpen );\r
3145     DeleteObject( hbrush );\r
3146 }\r
3147 \r
3148 BOOL HasHighlightInfo()\r
3149 {\r
3150     BOOL result = FALSE;\r
3151 \r
3152     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3153         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3154     {\r
3155         result = TRUE;\r
3156     }\r
3157 \r
3158     return result;\r
3159 }\r
3160 \r
3161 BOOL IsDrawArrowEnabled()\r
3162 {\r
3163     BOOL result = FALSE;\r
3164 \r
3165     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3166         result = TRUE;\r
3167     }\r
3168 \r
3169     return result;\r
3170 }\r
3171 \r
3172 VOID DrawArrowHighlight( HDC hdc )\r
3173 {\r
3174     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3175         DrawArrowBetweenSquares( hdc,\r
3176             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3177             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3178     }\r
3179 }\r
3180 \r
3181 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3182 {\r
3183     HRGN result = NULL;\r
3184 \r
3185     if( HasHighlightInfo() ) {\r
3186         int x1, y1, x2, y2;\r
3187         int sx, sy, dx, dy;\r
3188 \r
3189         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3190         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3191 \r
3192         sx = MIN( x1, x2 );\r
3193         sy = MIN( y1, y2 );\r
3194         dx = MAX( x1, x2 ) + squareSize;\r
3195         dy = MAX( y1, y2 ) + squareSize;\r
3196 \r
3197         result = CreateRectRgn( sx, sy, dx, dy );\r
3198     }\r
3199 \r
3200     return result;\r
3201 }\r
3202 \r
3203 /*\r
3204     Warning: this function modifies the behavior of several other functions. \r
3205     \r
3206     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3207     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3208     repaint is scattered all over the place, which is not good for features such as\r
3209     "arrow highlighting" that require a full repaint of the board.\r
3210 \r
3211     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3212     user interaction, when speed is not so important) but especially to avoid errors\r
3213     in the displayed graphics.\r
3214 \r
3215     In such patched places, I always try refer to this function so there is a single\r
3216     place to maintain knowledge.\r
3217     \r
3218     To restore the original behavior, just return FALSE unconditionally.\r
3219 */\r
3220 BOOL IsFullRepaintPreferrable()\r
3221 {\r
3222     BOOL result = FALSE;\r
3223 \r
3224     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3225         /* Arrow may appear on the board */\r
3226         result = TRUE;\r
3227     }\r
3228 \r
3229     return result;\r
3230 }\r
3231 \r
3232 /* \r
3233     This function is called by DrawPosition to know whether a full repaint must\r
3234     be forced or not.\r
3235 \r
3236     Only DrawPosition may directly call this function, which makes use of \r
3237     some state information. Other function should call DrawPosition specifying \r
3238     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3239 */\r
3240 BOOL DrawPositionNeedsFullRepaint()\r
3241 {\r
3242     BOOL result = FALSE;\r
3243 \r
3244     /* \r
3245         Probably a slightly better policy would be to trigger a full repaint\r
3246         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3247         but animation is fast enough that it's difficult to notice.\r
3248     */\r
3249     if( animInfo.piece == EmptySquare ) {\r
3250         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3251             result = TRUE;\r
3252         }\r
3253     }\r
3254 \r
3255     return result;\r
3256 }\r
3257 \r
3258 VOID\r
3259 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3260 {\r
3261   int row, column, x, y, square_color, piece_color;\r
3262   ChessSquare piece;\r
3263   HBRUSH oldBrush;\r
3264   HDC texture_hdc = NULL;\r
3265 \r
3266   /* [AS] Initialize background textures if needed */\r
3267   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3268       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3269       if( backTextureSquareSize != squareSize \r
3270        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3271           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3272           backTextureSquareSize = squareSize;\r
3273           RebuildTextureSquareInfo();\r
3274       }\r
3275 \r
3276       texture_hdc = CreateCompatibleDC( hdc );\r
3277   }\r
3278 \r
3279   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3280     for (column = 0; column < BOARD_WIDTH; column++) {\r
3281   \r
3282       SquareToPos(row, column, &x, &y);\r
3283 \r
3284       piece = board[row][column];\r
3285 \r
3286       square_color = ((column + row) % 2) == 1;\r
3287       if( gameInfo.variant == VariantXiangqi ) {\r
3288           square_color = !InPalace(row, column);\r
3289           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3290           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3291       }\r
3292       piece_color = (int) piece < (int) BlackPawn;\r
3293 \r
3294 \r
3295       /* [HGM] holdings file: light square or black */\r
3296       if(column == BOARD_LEFT-2) {\r
3297             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3298                 square_color = 1;\r
3299             else {\r
3300                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3301                 continue;\r
3302             }\r
3303       } else\r
3304       if(column == BOARD_RGHT + 1 ) {\r
3305             if( row < gameInfo.holdingsSize )\r
3306                 square_color = 1;\r
3307             else {\r
3308                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3309                 continue;\r
3310             }\r
3311       }\r
3312       if(column == BOARD_LEFT-1 ) /* left align */\r
3313             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3314       else if( column == BOARD_RGHT) /* right align */\r
3315             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3316       else\r
3317       if (appData.monoMode) {\r
3318         if (piece == EmptySquare) {\r
3319           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3320                  square_color ? WHITENESS : BLACKNESS);\r
3321         } else {\r
3322           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3323         }\r
3324       } \r
3325       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3326           /* [AS] Draw the square using a texture bitmap */\r
3327           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3328           int r = row, c = column; // [HGM] do not flip board in flipView\r
3329           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3330 \r
3331           DrawTile( x, y, \r
3332               squareSize, squareSize, \r
3333               hdc, \r
3334               texture_hdc,\r
3335               backTextureSquareInfo[r][c].mode,\r
3336               backTextureSquareInfo[r][c].x,\r
3337               backTextureSquareInfo[r][c].y );\r
3338 \r
3339           SelectObject( texture_hdc, hbm );\r
3340 \r
3341           if (piece != EmptySquare) {\r
3342               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3343           }\r
3344       }\r
3345       else {\r
3346         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3347 \r
3348         oldBrush = SelectObject(hdc, brush );\r
3349         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3350         SelectObject(hdc, oldBrush);\r
3351         if (piece != EmptySquare)\r
3352           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3353       }\r
3354     }\r
3355   }\r
3356 \r
3357   if( texture_hdc != NULL ) {\r
3358     DeleteDC( texture_hdc );\r
3359   }\r
3360 }\r
3361 \r
3362 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3363 void fputDW(FILE *f, int x)\r
3364 {\r
3365         fputc(x     & 255, f);\r
3366         fputc(x>>8  & 255, f);\r
3367         fputc(x>>16 & 255, f);\r
3368         fputc(x>>24 & 255, f);\r
3369 }\r
3370 \r
3371 #define MAX_CLIPS 200   /* more than enough */\r
3372 \r
3373 VOID\r
3374 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3375 {\r
3376 //  HBITMAP bufferBitmap;\r
3377   BITMAP bi;\r
3378 //  RECT Rect;\r
3379   HDC tmphdc;\r
3380   HBITMAP hbm;\r
3381   int w = 100, h = 50;\r
3382 \r
3383   if(logo == NULL) return;\r
3384 //  GetClientRect(hwndMain, &Rect);\r
3385 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3386 //                                      Rect.bottom-Rect.top+1);\r
3387   tmphdc = CreateCompatibleDC(hdc);\r
3388   hbm = SelectObject(tmphdc, logo);\r
3389   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3390             w = bi.bmWidth;\r
3391             h = bi.bmHeight;\r
3392   }\r
3393   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3394                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3395   SelectObject(tmphdc, hbm);\r
3396   DeleteDC(tmphdc);\r
3397 }\r
3398 \r
3399 VOID\r
3400 DisplayLogos()\r
3401 {\r
3402   if(logoHeight) {\r
3403         HDC hdc = GetDC(hwndMain);\r
3404         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3405         if(appData.autoLogo) {\r
3406           \r
3407           switch(gameMode) { // pick logos based on game mode\r
3408             case IcsObserving:\r
3409                 whiteLogo = second.programLogo; // ICS logo\r
3410                 blackLogo = second.programLogo;\r
3411             default:\r
3412                 break;\r
3413             case IcsPlayingWhite:\r
3414                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3415                 blackLogo = second.programLogo; // ICS logo\r
3416                 break;\r
3417             case IcsPlayingBlack:\r
3418                 whiteLogo = second.programLogo; // ICS logo\r
3419                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3420                 break;\r
3421             case TwoMachinesPlay:\r
3422                 if(first.twoMachinesColor[0] == 'b') {\r
3423                     whiteLogo = second.programLogo;\r
3424                     blackLogo = first.programLogo;\r
3425                 }\r
3426                 break;\r
3427             case MachinePlaysWhite:\r
3428                 blackLogo = userLogo;\r
3429                 break;\r
3430             case MachinePlaysBlack:\r
3431                 whiteLogo = userLogo;\r
3432                 blackLogo = first.programLogo;\r
3433           }\r
3434         }\r
3435         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3436         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3437         ReleaseDC(hwndMain, hdc);\r
3438   }\r
3439 }\r
3440 \r
3441 static HDC hdcSeek;\r
3442 \r
3443 // [HGM] seekgraph\r
3444 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3445 {\r
3446     POINT stPt;\r
3447     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3448     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3449     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3450     SelectObject( hdcSeek, hp );\r
3451 }\r
3452 \r
3453 // front-end wrapper for drawing functions to do rectangles\r
3454 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3455 {\r
3456     HPEN hp;\r
3457     RECT rc;\r
3458 \r
3459     if (hdcSeek == NULL) {\r
3460     hdcSeek = GetDC(hwndMain);\r
3461       if (!appData.monoMode) {\r
3462         SelectPalette(hdcSeek, hPal, FALSE);\r
3463         RealizePalette(hdcSeek);\r
3464       }\r
3465     }\r
3466     hp = SelectObject( hdcSeek, gridPen );\r
3467     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3468     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3469     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3470     SelectObject( hdcSeek, hp );\r
3471 }\r
3472 \r
3473 // front-end wrapper for putting text in graph\r
3474 void DrawSeekText(char *buf, int x, int y)\r
3475 {\r
3476         SIZE stSize;\r
3477         SetBkMode( hdcSeek, TRANSPARENT );\r
3478         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3479         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3480 }\r
3481 \r
3482 void DrawSeekDot(int x, int y, int color)\r
3483 {\r
3484         int square = color & 0x80;\r
3485         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3486                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3487         color &= 0x7F;\r
3488         if(square)\r
3489             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3490                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3491         else\r
3492             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3493                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3494             SelectObject(hdcSeek, oldBrush);\r
3495 }\r
3496 \r
3497 VOID\r
3498 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3499 {\r
3500   static Board lastReq[2], lastDrawn[2];\r
3501   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3502   static int lastDrawnFlipView = 0;\r
3503   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3504   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3505   HDC tmphdc;\r
3506   HDC hdcmem;\r
3507   HBITMAP bufferBitmap;\r
3508   HBITMAP oldBitmap;\r
3509   RECT Rect;\r
3510   HRGN clips[MAX_CLIPS];\r
3511   ChessSquare dragged_piece = EmptySquare;\r
3512   int nr = twoBoards*partnerUp;\r
3513 \r
3514   /* I'm undecided on this - this function figures out whether a full\r
3515    * repaint is necessary on its own, so there's no real reason to have the\r
3516    * caller tell it that.  I think this can safely be set to FALSE - but\r
3517    * if we trust the callers not to request full repaints unnessesarily, then\r
3518    * we could skip some clipping work.  In other words, only request a full\r
3519    * redraw when the majority of pieces have changed positions (ie. flip, \r
3520    * gamestart and similar)  --Hawk\r
3521    */\r
3522   Boolean fullrepaint = repaint;\r
3523 \r
3524   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3525 \r
3526   if( DrawPositionNeedsFullRepaint() ) {\r
3527       fullrepaint = TRUE;\r
3528   }\r
3529 \r
3530   if (board == NULL) {\r
3531     if (!lastReqValid[nr]) {\r
3532       return;\r
3533     }\r
3534     board = lastReq[nr];\r
3535   } else {\r
3536     CopyBoard(lastReq[nr], board);\r
3537     lastReqValid[nr] = 1;\r
3538   }\r
3539 \r
3540   if (doingSizing) {\r
3541     return;\r
3542   }\r
3543 \r
3544   if (IsIconic(hwndMain)) {\r
3545     return;\r
3546   }\r
3547 \r
3548   if (hdc == NULL) {\r
3549     hdc = GetDC(hwndMain);\r
3550     if (!appData.monoMode) {\r
3551       SelectPalette(hdc, hPal, FALSE);\r
3552       RealizePalette(hdc);\r
3553     }\r
3554     releaseDC = TRUE;\r
3555   } else {\r
3556     releaseDC = FALSE;\r
3557   }\r
3558 \r
3559   /* Create some work-DCs */\r
3560   hdcmem = CreateCompatibleDC(hdc);\r
3561   tmphdc = CreateCompatibleDC(hdc);\r
3562 \r
3563   /* If dragging is in progress, we temporarely remove the piece */\r
3564   /* [HGM] or temporarily decrease count if stacked              */\r
3565   /*       !! Moved to before board compare !!                   */\r
3566   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3567     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3568     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3569             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3570         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3571     } else \r
3572     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3573             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3574         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3575     } else \r
3576         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3577   }\r
3578 \r
3579   /* Figure out which squares need updating by comparing the \r
3580    * newest board with the last drawn board and checking if\r
3581    * flipping has changed.\r
3582    */\r
3583   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3584     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3585       for (column = 0; column < BOARD_WIDTH; column++) {\r
3586         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3587           SquareToPos(row, column, &x, &y);\r
3588           clips[num_clips++] =\r
3589             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3590         }\r
3591       }\r
3592     }\r
3593    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3594     for (i=0; i<2; i++) {\r
3595       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3596           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3597         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3598             lastDrawnHighlight.sq[i].y >= 0) {\r
3599           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3600                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3601           clips[num_clips++] =\r
3602             CreateRectRgn(x - lineGap, y - lineGap, \r
3603                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3604         }\r
3605         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3606           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3607           clips[num_clips++] =\r
3608             CreateRectRgn(x - lineGap, y - lineGap, \r
3609                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3610         }\r
3611       }\r
3612     }\r
3613     for (i=0; i<2; i++) {\r
3614       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3615           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3616         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3617             lastDrawnPremove.sq[i].y >= 0) {\r
3618           SquareToPos(lastDrawnPremove.sq[i].y,\r
3619                       lastDrawnPremove.sq[i].x, &x, &y);\r
3620           clips[num_clips++] =\r
3621             CreateRectRgn(x - lineGap, y - lineGap, \r
3622                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3623         }\r
3624         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3625             premoveHighlightInfo.sq[i].y >= 0) {\r
3626           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3627                       premoveHighlightInfo.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    } else { // nr == 1\r
3635         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3636         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3637         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3638         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3639       for (i=0; i<2; i++) {\r
3640         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3641             partnerHighlightInfo.sq[i].y >= 0) {\r
3642           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3643                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3644           clips[num_clips++] =\r
3645             CreateRectRgn(x - lineGap, y - lineGap, \r
3646                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3647         }\r
3648         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3649             oldPartnerHighlight.sq[i].y >= 0) {\r
3650           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3651                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3652           clips[num_clips++] =\r
3653             CreateRectRgn(x - lineGap, y - lineGap, \r
3654                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3655         }\r
3656       }\r
3657    }\r
3658   } else {\r
3659     fullrepaint = TRUE;\r
3660   }\r
3661 \r
3662   /* Create a buffer bitmap - this is the actual bitmap\r
3663    * being written to.  When all the work is done, we can\r
3664    * copy it to the real DC (the screen).  This avoids\r
3665    * the problems with flickering.\r
3666    */\r
3667   GetClientRect(hwndMain, &Rect);\r
3668   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3669                                         Rect.bottom-Rect.top+1);\r
3670   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3671   if (!appData.monoMode) {\r
3672     SelectPalette(hdcmem, hPal, FALSE);\r
3673   }\r
3674 \r
3675   /* Create clips for dragging */\r
3676   if (!fullrepaint) {\r
3677     if (dragInfo.from.x >= 0) {\r
3678       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3679       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3680     }\r
3681     if (dragInfo.start.x >= 0) {\r
3682       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3683       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3684     }\r
3685     if (dragInfo.pos.x >= 0) {\r
3686       x = dragInfo.pos.x - squareSize / 2;\r
3687       y = dragInfo.pos.y - squareSize / 2;\r
3688       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3689     }\r
3690     if (dragInfo.lastpos.x >= 0) {\r
3691       x = dragInfo.lastpos.x - squareSize / 2;\r
3692       y = dragInfo.lastpos.y - squareSize / 2;\r
3693       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3694     }\r
3695   }\r
3696 \r
3697   /* Are we animating a move?  \r
3698    * If so, \r
3699    *   - remove the piece from the board (temporarely)\r
3700    *   - calculate the clipping region\r
3701    */\r
3702   if (!fullrepaint) {\r
3703     if (animInfo.piece != EmptySquare) {\r
3704       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3705       x = boardRect.left + animInfo.lastpos.x;\r
3706       y = boardRect.top + animInfo.lastpos.y;\r
3707       x2 = boardRect.left + animInfo.pos.x;\r
3708       y2 = boardRect.top + animInfo.pos.y;\r
3709       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3710       /* Slight kludge.  The real problem is that after AnimateMove is\r
3711          done, the position on the screen does not match lastDrawn.\r
3712          This currently causes trouble only on e.p. captures in\r
3713          atomic, where the piece moves to an empty square and then\r
3714          explodes.  The old and new positions both had an empty square\r
3715          at the destination, but animation has drawn a piece there and\r
3716          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3717       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3718     }\r
3719   }\r
3720 \r
3721   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3722   if (num_clips == 0)\r
3723     fullrepaint = TRUE;\r
3724 \r
3725   /* Set clipping on the memory DC */\r
3726   if (!fullrepaint) {\r
3727     SelectClipRgn(hdcmem, clips[0]);\r
3728     for (x = 1; x < num_clips; x++) {\r
3729       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3730         abort();  // this should never ever happen!\r
3731     }\r
3732   }\r
3733 \r
3734   /* Do all the drawing to the memory DC */\r
3735   if(explodeInfo.radius) { // [HGM] atomic\r
3736         HBRUSH oldBrush;\r
3737         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3738         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3739         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3740         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3741         x += squareSize/2;\r
3742         y += squareSize/2;\r
3743         if(!fullrepaint) {\r
3744           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3745           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3746         }\r
3747         DrawGridOnDC(hdcmem);\r
3748         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3749         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3750         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3751         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3752         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3753         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3754         SelectObject(hdcmem, oldBrush);\r
3755   } else {\r
3756     DrawGridOnDC(hdcmem);\r
3757     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3758         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3759         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3760     } else {\r
3761         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3762         oldPartnerHighlight = partnerHighlightInfo;\r
3763     }\r
3764     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3765   }\r
3766   if(nr == 0) // [HGM] dual: markers only on left board\r
3767   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3768     for (column = 0; column < BOARD_WIDTH; column++) {\r
3769         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3770             HBRUSH oldBrush = SelectObject(hdcmem, \r
3771                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3772             SquareToPos(row, column, &x, &y);\r
3773             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3774                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3775             SelectObject(hdcmem, oldBrush);\r
3776         }\r
3777     }\r
3778   }\r
3779 \r
3780   if( appData.highlightMoveWithArrow ) {\r
3781     DrawArrowHighlight(hdcmem);\r
3782   }\r
3783 \r
3784   DrawCoordsOnDC(hdcmem);\r
3785 \r
3786   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3787                  /* to make sure lastDrawn contains what is actually drawn */\r
3788 \r
3789   /* Put the dragged piece back into place and draw it (out of place!) */\r
3790     if (dragged_piece != EmptySquare) {\r
3791     /* [HGM] or restack */\r
3792     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3793                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3794     else\r
3795     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3796                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3797     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3798     x = dragInfo.pos.x - squareSize / 2;\r
3799     y = dragInfo.pos.y - squareSize / 2;\r
3800     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3801                   ((int) dragInfo.piece < (int) BlackPawn), \r
3802                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3803   }   \r
3804   \r
3805   /* Put the animated piece back into place and draw it */\r
3806   if (animInfo.piece != EmptySquare) {\r
3807     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3808     x = boardRect.left + animInfo.pos.x;\r
3809     y = boardRect.top + animInfo.pos.y;\r
3810     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3811                   ((int) animInfo.piece < (int) BlackPawn),\r
3812                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3813   }\r
3814 \r
3815   /* Release the bufferBitmap by selecting in the old bitmap \r
3816    * and delete the memory DC\r
3817    */\r
3818   SelectObject(hdcmem, oldBitmap);\r
3819   DeleteDC(hdcmem);\r
3820 \r
3821   /* Set clipping on the target DC */\r
3822   if (!fullrepaint) {\r
3823     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3824         RECT rect;\r
3825         GetRgnBox(clips[x], &rect);\r
3826         DeleteObject(clips[x]);\r
3827         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3828                           rect.right + wpMain.width/2, rect.bottom);\r
3829     }\r
3830     SelectClipRgn(hdc, clips[0]);\r
3831     for (x = 1; x < num_clips; x++) {\r
3832       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3833         abort();   // this should never ever happen!\r
3834     } \r
3835   }\r
3836 \r
3837   /* Copy the new bitmap onto the screen in one go.\r
3838    * This way we avoid any flickering\r
3839    */\r
3840   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3841   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3842          boardRect.right - boardRect.left,\r
3843          boardRect.bottom - boardRect.top,\r
3844          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3845   if(saveDiagFlag) { \r
3846     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3847     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3848 \r
3849     GetObject(bufferBitmap, sizeof(b), &b);\r
3850     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3851         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3852         bih.biWidth = b.bmWidth;\r
3853         bih.biHeight = b.bmHeight;\r
3854         bih.biPlanes = 1;\r
3855         bih.biBitCount = b.bmBitsPixel;\r
3856         bih.biCompression = 0;\r
3857         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3858         bih.biXPelsPerMeter = 0;\r
3859         bih.biYPelsPerMeter = 0;\r
3860         bih.biClrUsed = 0;\r
3861         bih.biClrImportant = 0;\r
3862 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3863 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3864         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3865 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3866 \r
3867         wb = b.bmWidthBytes;\r
3868         // count colors\r
3869         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3870                 int k = ((int*) pData)[i];\r
3871                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3872                 if(j >= 16) break;\r
3873                 color[j] = k;\r
3874                 if(j >= nrColors) nrColors = j+1;\r
3875         }\r
3876         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3877                 INT p = 0;\r
3878                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3879                     for(w=0; w<(wb>>2); w+=2) {\r
3880                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3881                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3882                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3883                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3884                         pData[p++] = m | j<<4;\r
3885                     }\r
3886                     while(p&3) pData[p++] = 0;\r
3887                 }\r
3888                 fac = 3;\r
3889                 wb = ((wb+31)>>5)<<2;\r
3890         }\r
3891         // write BITMAPFILEHEADER\r
3892         fprintf(diagFile, "BM");\r
3893         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3894         fputDW(diagFile, 0);\r
3895         fputDW(diagFile, 0x36 + (fac?64:0));\r
3896         // write BITMAPINFOHEADER\r
3897         fputDW(diagFile, 40);\r
3898         fputDW(diagFile, b.bmWidth);\r
3899         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3900         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3901         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3902         fputDW(diagFile, 0);\r
3903         fputDW(diagFile, 0);\r
3904         fputDW(diagFile, 0);\r
3905         fputDW(diagFile, 0);\r
3906         fputDW(diagFile, 0);\r
3907         fputDW(diagFile, 0);\r
3908         // write color table\r
3909         if(fac)\r
3910         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3911         // write bitmap data\r
3912         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3913                 fputc(pData[i], diagFile);\r
3914         free(pData);\r
3915      }\r
3916   }\r
3917 \r
3918   SelectObject(tmphdc, oldBitmap);\r
3919 \r
3920   /* Massive cleanup */\r
3921   for (x = 0; x < num_clips; x++)\r
3922     DeleteObject(clips[x]);\r
3923 \r
3924   DeleteDC(tmphdc);\r
3925   DeleteObject(bufferBitmap);\r
3926 \r
3927   if (releaseDC) \r
3928     ReleaseDC(hwndMain, hdc);\r
3929   \r
3930   if (lastDrawnFlipView != flipView && nr == 0) {\r
3931     if (flipView)\r
3932       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3933     else\r
3934       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3935   }\r
3936 \r
3937 /*  CopyBoard(lastDrawn, board);*/\r
3938   lastDrawnHighlight = highlightInfo;\r
3939   lastDrawnPremove   = premoveHighlightInfo;\r
3940   lastDrawnFlipView = flipView;\r
3941   lastDrawnValid[nr] = 1;\r
3942 }\r
3943 \r
3944 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3945 int\r
3946 SaveDiagram(f)\r
3947      FILE *f;\r
3948 {\r
3949     saveDiagFlag = 1; diagFile = f;\r
3950     HDCDrawPosition(NULL, TRUE, NULL);\r
3951     saveDiagFlag = 0;\r
3952 \r
3953     fclose(f);\r
3954     return TRUE;\r
3955 }\r
3956 \r
3957 \r
3958 /*---------------------------------------------------------------------------*\\r
3959 | CLIENT PAINT PROCEDURE\r
3960 |   This is the main event-handler for the WM_PAINT message.\r
3961 |\r
3962 \*---------------------------------------------------------------------------*/\r
3963 VOID\r
3964 PaintProc(HWND hwnd)\r
3965 {\r
3966   HDC         hdc;\r
3967   PAINTSTRUCT ps;\r
3968   HFONT       oldFont;\r
3969 \r
3970   if((hdc = BeginPaint(hwnd, &ps))) {\r
3971     if (IsIconic(hwnd)) {\r
3972       DrawIcon(hdc, 2, 2, iconCurrent);\r
3973     } else {\r
3974       if (!appData.monoMode) {\r
3975         SelectPalette(hdc, hPal, FALSE);\r
3976         RealizePalette(hdc);\r
3977       }\r
3978       HDCDrawPosition(hdc, 1, NULL);\r
3979       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3980         flipView = !flipView; partnerUp = !partnerUp;\r
3981         HDCDrawPosition(hdc, 1, NULL);\r
3982         flipView = !flipView; partnerUp = !partnerUp;\r
3983       }\r
3984       oldFont =\r
3985         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3986       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3987                  ETO_CLIPPED|ETO_OPAQUE,\r
3988                  &messageRect, messageText, strlen(messageText), NULL);\r
3989       SelectObject(hdc, oldFont);\r
3990       DisplayBothClocks();\r
3991       DisplayLogos();\r
3992     }\r
3993     EndPaint(hwnd,&ps);\r
3994   }\r
3995 \r
3996   return;\r
3997 }\r
3998 \r
3999 \r
4000 /*\r
4001  * If the user selects on a border boundary, return -1; if off the board,\r
4002  *   return -2.  Otherwise map the event coordinate to the square.\r
4003  * The offset boardRect.left or boardRect.top must already have been\r
4004  *   subtracted from x.\r
4005  */\r
4006 int EventToSquare(x, limit)\r
4007      int x, limit;\r
4008 {\r
4009   if (x <= 0)\r
4010     return -2;\r
4011   if (x < lineGap)\r
4012     return -1;\r
4013   x -= lineGap;\r
4014   if ((x % (squareSize + lineGap)) >= squareSize)\r
4015     return -1;\r
4016   x /= (squareSize + lineGap);\r
4017     if (x >= limit)\r
4018     return -2;\r
4019   return x;\r
4020 }\r
4021 \r
4022 typedef struct {\r
4023   char piece;\r
4024   int command;\r
4025   char* name;\r
4026 } DropEnable;\r
4027 \r
4028 DropEnable dropEnables[] = {\r
4029   { 'P', DP_Pawn, N_("Pawn") },\r
4030   { 'N', DP_Knight, N_("Knight") },\r
4031   { 'B', DP_Bishop, N_("Bishop") },\r
4032   { 'R', DP_Rook, N_("Rook") },\r
4033   { 'Q', DP_Queen, N_("Queen") },\r
4034 };\r
4035 \r
4036 VOID\r
4037 SetupDropMenu(HMENU hmenu)\r
4038 {\r
4039   int i, count, enable;\r
4040   char *p;\r
4041   extern char white_holding[], black_holding[];\r
4042   char item[MSG_SIZ];\r
4043 \r
4044   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4045     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4046                dropEnables[i].piece);\r
4047     count = 0;\r
4048     while (p && *p++ == dropEnables[i].piece) count++;\r
4049       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4050     enable = count > 0 || !appData.testLegality\r
4051       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4052                       && !appData.icsActive);\r
4053     ModifyMenu(hmenu, dropEnables[i].command,\r
4054                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4055                dropEnables[i].command, item);\r
4056   }\r
4057 }\r
4058 \r
4059 void DragPieceBegin(int x, int y)\r
4060 {\r
4061       dragInfo.lastpos.x = boardRect.left + x;\r
4062       dragInfo.lastpos.y = boardRect.top + y;\r
4063       dragInfo.from.x = fromX;\r
4064       dragInfo.from.y = fromY;\r
4065       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4066       dragInfo.start = dragInfo.from;\r
4067       SetCapture(hwndMain);\r
4068 }\r
4069 \r
4070 void DragPieceEnd(int x, int y)\r
4071 {\r
4072     ReleaseCapture();\r
4073     dragInfo.start.x = dragInfo.start.y = -1;\r
4074     dragInfo.from = dragInfo.start;\r
4075     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4076 }\r
4077 \r
4078 void ChangeDragPiece(ChessSquare piece)\r
4079 {\r
4080     dragInfo.piece = piece;\r
4081 }\r
4082 \r
4083 /* Event handler for mouse messages */\r
4084 VOID\r
4085 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4086 {\r
4087   int x, y, menuNr;\r
4088   POINT pt;\r
4089   static int recursive = 0;\r
4090   HMENU hmenu;\r
4091   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4092 \r
4093   if (recursive) {\r
4094     if (message == WM_MBUTTONUP) {\r
4095       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4096          to the middle button: we simulate pressing the left button too!\r
4097          */\r
4098       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4099       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4100     }\r
4101     return;\r
4102   }\r
4103   recursive++;\r
4104   \r
4105   pt.x = LOWORD(lParam);\r
4106   pt.y = HIWORD(lParam);\r
4107   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4108   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4109   if (!flipView && y >= 0) {\r
4110     y = BOARD_HEIGHT - 1 - y;\r
4111   }\r
4112   if (flipView && x >= 0) {\r
4113     x = BOARD_WIDTH - 1 - x;\r
4114   }\r
4115 \r
4116   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4117 \r
4118   switch (message) {\r
4119   case WM_LBUTTONDOWN:\r
4120       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4121         ClockClick(flipClock);\r
4122       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4123         ClockClick(!flipClock);\r
4124       }\r
4125       dragInfo.start.x = dragInfo.start.y = -1;\r
4126       dragInfo.from = dragInfo.start;\r
4127     if(fromX == -1 && frozen) { // not sure where this is for\r
4128                 fromX = fromY = -1; \r
4129       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4130       break;\r
4131     }\r
4132       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4133       DrawPosition(TRUE, NULL);\r
4134     break;\r
4135 \r
4136   case WM_LBUTTONUP:\r
4137       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4138       DrawPosition(TRUE, NULL);\r
4139     break;\r
4140 \r
4141   case WM_MOUSEMOVE:\r
4142     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4143     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4144     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4145     if ((appData.animateDragging || appData.highlightDragging)\r
4146         && (wParam & MK_LBUTTON)\r
4147         && dragInfo.from.x >= 0) \r
4148     {\r
4149       BOOL full_repaint = FALSE;\r
4150 \r
4151       if (appData.animateDragging) {\r
4152         dragInfo.pos = pt;\r
4153       }\r
4154       if (appData.highlightDragging) {\r
4155         SetHighlights(fromX, fromY, x, y);\r
4156         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4157             full_repaint = TRUE;\r
4158         }\r
4159       }\r
4160       \r
4161       DrawPosition( full_repaint, NULL);\r
4162       \r
4163       dragInfo.lastpos = dragInfo.pos;\r
4164     }\r
4165     break;\r
4166 \r
4167   case WM_MOUSEWHEEL: // [DM]\r
4168     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4169        /* Mouse Wheel is being rolled forward\r
4170         * Play moves forward\r
4171         */\r
4172        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4173                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4174        /* Mouse Wheel is being rolled backward\r
4175         * Play moves backward\r
4176         */\r
4177        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4178                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4179     }\r
4180     break;\r
4181 \r
4182   case WM_MBUTTONUP:\r
4183   case WM_RBUTTONUP:\r
4184     ReleaseCapture();\r
4185     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4186     break;\r
4187  \r
4188   case WM_MBUTTONDOWN:\r
4189   case WM_RBUTTONDOWN:\r
4190     ErrorPopDown();\r
4191     ReleaseCapture();\r
4192     fromX = fromY = -1;\r
4193     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4194     dragInfo.start.x = dragInfo.start.y = -1;\r
4195     dragInfo.from = dragInfo.start;\r
4196     dragInfo.lastpos = dragInfo.pos;\r
4197     if (appData.highlightDragging) {\r
4198       ClearHighlights();\r
4199     }\r
4200     if(y == -2) {\r
4201       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4202       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4203           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4204       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4205           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4206       }\r
4207       break;\r
4208     }\r
4209     DrawPosition(TRUE, NULL);\r
4210 \r
4211     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4212     switch (menuNr) {\r
4213     case 0:\r
4214       if (message == WM_MBUTTONDOWN) {\r
4215         buttonCount = 3;  /* even if system didn't think so */\r
4216         if (wParam & MK_SHIFT) \r
4217           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4218         else\r
4219           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4220       } else { /* message == WM_RBUTTONDOWN */\r
4221         /* Just have one menu, on the right button.  Windows users don't\r
4222            think to try the middle one, and sometimes other software steals\r
4223            it, or it doesn't really exist. */\r
4224         if(gameInfo.variant != VariantShogi)\r
4225             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4226         else\r
4227             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4228       }\r
4229       break;\r
4230     case 2:\r
4231       SetCapture(hwndMain);
4232       break;\r
4233     case 1:\r
4234       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4235       SetupDropMenu(hmenu);\r
4236       MenuPopup(hwnd, pt, hmenu, -1);\r
4237     default:\r
4238       break;\r
4239     }\r
4240     break;\r
4241   }\r
4242 \r
4243   recursive--;\r
4244 }\r
4245 \r
4246 /* Preprocess messages for buttons in main window */\r
4247 LRESULT CALLBACK\r
4248 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4249 {\r
4250   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4251   int i, dir;\r
4252 \r
4253   for (i=0; i<N_BUTTONS; i++) {\r
4254     if (buttonDesc[i].id == id) break;\r
4255   }\r
4256   if (i == N_BUTTONS) return 0;\r
4257   switch (message) {\r
4258   case WM_KEYDOWN:\r
4259     switch (wParam) {\r
4260     case VK_LEFT:\r
4261     case VK_RIGHT:\r
4262       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4263       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4264       return TRUE;\r
4265     }\r
4266     break;\r
4267   case WM_CHAR:\r
4268     switch (wParam) {\r
4269     case '\r':\r
4270       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4271       return TRUE;\r
4272     default:\r
4273       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4274         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4275         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4276         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4277         SetFocus(h);\r
4278         SendMessage(h, WM_CHAR, wParam, lParam);\r
4279         return TRUE;\r
4280       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4281         TypeInEvent((char)wParam);\r
4282       }\r
4283       break;\r
4284     }\r
4285     break;\r
4286   }\r
4287   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4288 }\r
4289 \r
4290 /* Process messages for Promotion dialog box */\r
4291 LRESULT CALLBACK\r
4292 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4293 {\r
4294   char promoChar;\r
4295 \r
4296   switch (message) {\r
4297   case WM_INITDIALOG: /* message: initialize dialog box */\r
4298     /* Center the dialog over the application window */\r
4299     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4300     Translate(hDlg, DLG_PromotionKing);\r
4301     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4302       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4303        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4304        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4305                SW_SHOW : SW_HIDE);\r
4306     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4307     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4308        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4309          PieceToChar(WhiteAngel) != '~') ||\r
4310         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4311          PieceToChar(BlackAngel) != '~')   ) ?\r
4312                SW_SHOW : SW_HIDE);\r
4313     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4314        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4315          PieceToChar(WhiteMarshall) != '~') ||\r
4316         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4317          PieceToChar(BlackMarshall) != '~')   ) ?\r
4318                SW_SHOW : SW_HIDE);\r
4319     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4320     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4321        gameInfo.variant != VariantShogi ?\r
4322                SW_SHOW : SW_HIDE);\r
4323     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4324        gameInfo.variant != VariantShogi ?\r
4325                SW_SHOW : SW_HIDE);\r
4326     if(gameInfo.variant == VariantShogi) {\r
4327         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4328         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4329         SetWindowText(hDlg, "Promote?");\r
4330     }\r
4331     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4332        gameInfo.variant == VariantSuper ?\r
4333                SW_SHOW : SW_HIDE);\r
4334     return TRUE;\r
4335 \r
4336   case WM_COMMAND: /* message: received a command */\r
4337     switch (LOWORD(wParam)) {\r
4338     case IDCANCEL:\r
4339       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4340       ClearHighlights();\r
4341       DrawPosition(FALSE, NULL);\r
4342       return TRUE;\r
4343     case PB_King:\r
4344       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4345       break;\r
4346     case PB_Queen:\r
4347       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4348       break;\r
4349     case PB_Rook:\r
4350       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4351       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4352       break;\r
4353     case PB_Bishop:\r
4354       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4355       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4356       break;\r
4357     case PB_Chancellor:\r
4358       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4359       break;\r
4360     case PB_Archbishop:\r
4361       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4362       break;\r
4363     case PB_Knight:\r
4364       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4365       break;\r
4366     default:\r
4367       return FALSE;\r
4368     }\r
4369     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4370     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4371     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4372     fromX = fromY = -1;\r
4373     if (!appData.highlightLastMove) {\r
4374       ClearHighlights();\r
4375       DrawPosition(FALSE, NULL);\r
4376     }\r
4377     return TRUE;\r
4378   }\r
4379   return FALSE;\r
4380 }\r
4381 \r
4382 /* Pop up promotion dialog */\r
4383 VOID\r
4384 PromotionPopup(HWND hwnd)\r
4385 {\r
4386   FARPROC lpProc;\r
4387 \r
4388   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4389   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4390     hwnd, (DLGPROC)lpProc);\r
4391   FreeProcInstance(lpProc);\r
4392 }\r
4393 \r
4394 void\r
4395 PromotionPopUp()\r
4396 {\r
4397   DrawPosition(TRUE, NULL);\r
4398   PromotionPopup(hwndMain);\r
4399 }\r
4400 \r
4401 /* Toggle ShowThinking */\r
4402 VOID\r
4403 ToggleShowThinking()\r
4404 {\r
4405   appData.showThinking = !appData.showThinking;\r
4406   ShowThinkingEvent();\r
4407 }\r
4408 \r
4409 VOID\r
4410 LoadGameDialog(HWND hwnd, char* title)\r
4411 {\r
4412   UINT number = 0;\r
4413   FILE *f;\r
4414   char fileTitle[MSG_SIZ];\r
4415   f = OpenFileDialog(hwnd, "rb", "",\r
4416                      appData.oldSaveStyle ? "gam" : "pgn",\r
4417                      GAME_FILT,\r
4418                      title, &number, fileTitle, NULL);\r
4419   if (f != NULL) {\r
4420     cmailMsgLoaded = FALSE;\r
4421     if (number == 0) {\r
4422       int error = GameListBuild(f);\r
4423       if (error) {\r
4424         DisplayError(_("Cannot build game list"), error);\r
4425       } else if (!ListEmpty(&gameList) &&\r
4426                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4427         GameListPopUp(f, fileTitle);\r
4428         return;\r
4429       }\r
4430       GameListDestroy();\r
4431       number = 1;\r
4432     }\r
4433     LoadGame(f, number, fileTitle, FALSE);\r
4434   }\r
4435 }\r
4436 \r
4437 int get_term_width()\r
4438 {\r
4439     HDC hdc;\r
4440     TEXTMETRIC tm;\r
4441     RECT rc;\r
4442     HFONT hfont, hold_font;\r
4443     LOGFONT lf;\r
4444     HWND hText;\r
4445 \r
4446     if (hwndConsole)\r
4447         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4448     else\r
4449         return 79;\r
4450 \r
4451     // get the text metrics\r
4452     hdc = GetDC(hText);\r
4453     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4454     if (consoleCF.dwEffects & CFE_BOLD)\r
4455         lf.lfWeight = FW_BOLD;\r
4456     if (consoleCF.dwEffects & CFE_ITALIC)\r
4457         lf.lfItalic = TRUE;\r
4458     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4459         lf.lfStrikeOut = TRUE;\r
4460     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4461         lf.lfUnderline = TRUE;\r
4462     hfont = CreateFontIndirect(&lf);\r
4463     hold_font = SelectObject(hdc, hfont);\r
4464     GetTextMetrics(hdc, &tm);\r
4465     SelectObject(hdc, hold_font);\r
4466     DeleteObject(hfont);\r
4467     ReleaseDC(hText, hdc);\r
4468 \r
4469     // get the rectangle\r
4470     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4471 \r
4472     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4473 }\r
4474 \r
4475 void UpdateICSWidth(HWND hText)\r
4476 {\r
4477     LONG old_width, new_width;\r
4478 \r
4479     new_width = get_term_width(hText, FALSE);\r
4480     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4481     if (new_width != old_width)\r
4482     {\r
4483         ics_update_width(new_width);\r
4484         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4485     }\r
4486 }\r
4487 \r
4488 VOID\r
4489 ChangedConsoleFont()\r
4490 {\r
4491   CHARFORMAT cfmt;\r
4492   CHARRANGE tmpsel, sel;\r
4493   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4494   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4495   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4496   PARAFORMAT paraf;\r
4497 \r
4498   cfmt.cbSize = sizeof(CHARFORMAT);\r
4499   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4500     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4501                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4502   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4503    * size.  This was undocumented in the version of MSVC++ that I had\r
4504    * when I wrote the code, but is apparently documented now.\r
4505    */\r
4506   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4507   cfmt.bCharSet = f->lf.lfCharSet;\r
4508   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4509   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4510   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4511   /* Why are the following seemingly needed too? */\r
4512   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4513   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4514   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4515   tmpsel.cpMin = 0;\r
4516   tmpsel.cpMax = -1; /*999999?*/\r
4517   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4518   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4519   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4520    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4521    */\r
4522   paraf.cbSize = sizeof(paraf);\r
4523   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4524   paraf.dxStartIndent = 0;\r
4525   paraf.dxOffset = WRAP_INDENT;\r
4526   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4527   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4528   UpdateICSWidth(hText);\r
4529 }\r
4530 \r
4531 /*---------------------------------------------------------------------------*\\r
4532  *\r
4533  * Window Proc for main window\r
4534  *\r
4535 \*---------------------------------------------------------------------------*/\r
4536 \r
4537 /* Process messages for main window, etc. */\r
4538 LRESULT CALLBACK\r
4539 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4540 {\r
4541   FARPROC lpProc;\r
4542   int wmId, wmEvent;\r
4543   char *defName;\r
4544   FILE *f;\r
4545   UINT number;\r
4546   char fileTitle[MSG_SIZ];\r
4547   char buf[MSG_SIZ];\r
4548   static SnapData sd;\r
4549 \r
4550   switch (message) {\r
4551 \r
4552   case WM_PAINT: /* message: repaint portion of window */\r
4553     PaintProc(hwnd);\r
4554     break;\r
4555 \r
4556   case WM_ERASEBKGND:\r
4557     if (IsIconic(hwnd)) {\r
4558       /* Cheat; change the message */\r
4559       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4560     } else {\r
4561       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4562     }\r
4563     break;\r
4564 \r
4565   case WM_LBUTTONDOWN:\r
4566   case WM_MBUTTONDOWN:\r
4567   case WM_RBUTTONDOWN:\r
4568   case WM_LBUTTONUP:\r
4569   case WM_MBUTTONUP:\r
4570   case WM_RBUTTONUP:\r
4571   case WM_MOUSEMOVE:\r
4572   case WM_MOUSEWHEEL:\r
4573     MouseEvent(hwnd, message, wParam, lParam);\r
4574     break;\r
4575 \r
4576   JAWS_KB_NAVIGATION\r
4577 \r
4578   case WM_CHAR:\r
4579     \r
4580     JAWS_ALT_INTERCEPT\r
4581 \r
4582     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4583         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4584         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4585         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4586         SetFocus(h);\r
4587         SendMessage(h, message, wParam, lParam);\r
4588     } else if(lParam != KF_REPEAT) {\r
4589         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4590                 TypeInEvent((char)wParam);\r
4591         } else if((char)wParam == 003) CopyGameToClipboard();\r
4592          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4593     }\r
4594 \r
4595     break;\r
4596 \r
4597   case WM_PALETTECHANGED:\r
4598     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4599       int nnew;\r
4600       HDC hdc = GetDC(hwndMain);\r
4601       SelectPalette(hdc, hPal, TRUE);\r
4602       nnew = RealizePalette(hdc);\r
4603       if (nnew > 0) {\r
4604         paletteChanged = TRUE;\r
4605         InvalidateRect(hwnd, &boardRect, FALSE);\r
4606       }\r
4607       ReleaseDC(hwnd, hdc);\r
4608     }\r
4609     break;\r
4610 \r
4611   case WM_QUERYNEWPALETTE:\r
4612     if (!appData.monoMode /*&& paletteChanged*/) {\r
4613       int nnew;\r
4614       HDC hdc = GetDC(hwndMain);\r
4615       paletteChanged = FALSE;\r
4616       SelectPalette(hdc, hPal, FALSE);\r
4617       nnew = RealizePalette(hdc);\r
4618       if (nnew > 0) {\r
4619         InvalidateRect(hwnd, &boardRect, FALSE);\r
4620       }\r
4621       ReleaseDC(hwnd, hdc);\r
4622       return TRUE;\r
4623     }\r
4624     return FALSE;\r
4625 \r
4626   case WM_COMMAND: /* message: command from application menu */\r
4627     wmId    = LOWORD(wParam);\r
4628     wmEvent = HIWORD(wParam);\r
4629 \r
4630     switch (wmId) {\r
4631     case IDM_NewGame:\r
4632       ResetGameEvent();\r
4633       SAY("new game enter a move to play against the computer with white");\r
4634       break;\r
4635 \r
4636     case IDM_NewGameFRC:\r
4637       if( NewGameFRC() == 0 ) {\r
4638         ResetGameEvent();\r
4639       }\r
4640       break;\r
4641 \r
4642     case IDM_NewVariant:\r
4643       NewVariantPopup(hwnd);\r
4644       break;\r
4645 \r
4646     case IDM_LoadGame:\r
4647       LoadGameDialog(hwnd, _("Load Game from File"));\r
4648       break;\r
4649 \r
4650     case IDM_LoadNextGame:\r
4651       ReloadGame(1);\r
4652       break;\r
4653 \r
4654     case IDM_LoadPrevGame:\r
4655       ReloadGame(-1);\r
4656       break;\r
4657 \r
4658     case IDM_ReloadGame:\r
4659       ReloadGame(0);\r
4660       break;\r
4661 \r
4662     case IDM_LoadPosition:\r
4663       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4664         Reset(FALSE, TRUE);\r
4665       }\r
4666       number = 1;\r
4667       f = OpenFileDialog(hwnd, "rb", "",\r
4668                          appData.oldSaveStyle ? "pos" : "fen",\r
4669                          POSITION_FILT,\r
4670                          _("Load Position from File"), &number, fileTitle, NULL);\r
4671       if (f != NULL) {\r
4672         LoadPosition(f, number, fileTitle);\r
4673       }\r
4674       break;\r
4675 \r
4676     case IDM_LoadNextPosition:\r
4677       ReloadPosition(1);\r
4678       break;\r
4679 \r
4680     case IDM_LoadPrevPosition:\r
4681       ReloadPosition(-1);\r
4682       break;\r
4683 \r
4684     case IDM_ReloadPosition:\r
4685       ReloadPosition(0);\r
4686       break;\r
4687 \r
4688     case IDM_SaveGame:\r
4689       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4690       f = OpenFileDialog(hwnd, "a", defName,\r
4691                          appData.oldSaveStyle ? "gam" : "pgn",\r
4692                          GAME_FILT,\r
4693                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4694       if (f != NULL) {\r
4695         SaveGame(f, 0, "");\r
4696       }\r
4697       break;\r
4698 \r
4699     case IDM_SavePosition:\r
4700       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4701       f = OpenFileDialog(hwnd, "a", defName,\r
4702                          appData.oldSaveStyle ? "pos" : "fen",\r
4703                          POSITION_FILT,\r
4704                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4705       if (f != NULL) {\r
4706         SavePosition(f, 0, "");\r
4707       }\r
4708       break;\r
4709 \r
4710     case IDM_SaveDiagram:\r
4711       defName = "diagram";\r
4712       f = OpenFileDialog(hwnd, "wb", defName,\r
4713                          "bmp",\r
4714                          DIAGRAM_FILT,\r
4715                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4716       if (f != NULL) {\r
4717         SaveDiagram(f);\r
4718       }\r
4719       break;\r
4720 \r
4721     case IDM_CopyGame:\r
4722       CopyGameToClipboard();\r
4723       break;\r
4724 \r
4725     case IDM_PasteGame:\r
4726       PasteGameFromClipboard();\r
4727       break;\r
4728 \r
4729     case IDM_CopyGameListToClipboard:\r
4730       CopyGameListToClipboard();\r
4731       break;\r
4732 \r
4733     /* [AS] Autodetect FEN or PGN data */\r
4734     case IDM_PasteAny:\r
4735       PasteGameOrFENFromClipboard();\r
4736       break;\r
4737 \r
4738     /* [AS] Move history */\r
4739     case IDM_ShowMoveHistory:\r
4740         if( MoveHistoryIsUp() ) {\r
4741             MoveHistoryPopDown();\r
4742         }\r
4743         else {\r
4744             MoveHistoryPopUp();\r
4745         }\r
4746         break;\r
4747 \r
4748     /* [AS] Eval graph */\r
4749     case IDM_ShowEvalGraph:\r
4750         if( EvalGraphIsUp() ) {\r
4751             EvalGraphPopDown();\r
4752         }\r
4753         else {\r
4754             EvalGraphPopUp();\r
4755             SetFocus(hwndMain);\r
4756         }\r
4757         break;\r
4758 \r
4759     /* [AS] Engine output */\r
4760     case IDM_ShowEngineOutput:\r
4761         if( EngineOutputIsUp() ) {\r
4762             EngineOutputPopDown();\r
4763         }\r
4764         else {\r
4765             EngineOutputPopUp();\r
4766         }\r
4767         break;\r
4768 \r
4769     /* [AS] User adjudication */\r
4770     case IDM_UserAdjudication_White:\r
4771         UserAdjudicationEvent( +1 );\r
4772         break;\r
4773 \r
4774     case IDM_UserAdjudication_Black:\r
4775         UserAdjudicationEvent( -1 );\r
4776         break;\r
4777 \r
4778     case IDM_UserAdjudication_Draw:\r
4779         UserAdjudicationEvent( 0 );\r
4780         break;\r
4781 \r
4782     /* [AS] Game list options dialog */\r
4783     case IDM_GameListOptions:\r
4784       GameListOptions();\r
4785       break;\r
4786 \r
4787     case IDM_NewChat:\r
4788       ChatPopUp(NULL);\r
4789       break;\r
4790 \r
4791     case IDM_CopyPosition:\r
4792       CopyFENToClipboard();\r
4793       break;\r
4794 \r
4795     case IDM_PastePosition:\r
4796       PasteFENFromClipboard();\r
4797       break;\r
4798 \r
4799     case IDM_MailMove:\r
4800       MailMoveEvent();\r
4801       break;\r
4802 \r
4803     case IDM_ReloadCMailMsg:\r
4804       Reset(TRUE, TRUE);\r
4805       ReloadCmailMsgEvent(FALSE);\r
4806       break;\r
4807 \r
4808     case IDM_Minimize:\r
4809       ShowWindow(hwnd, SW_MINIMIZE);\r
4810       break;\r
4811 \r
4812     case IDM_Exit:\r
4813       ExitEvent(0);\r
4814       break;\r
4815 \r
4816     case IDM_MachineWhite:\r
4817       MachineWhiteEvent();\r
4818       /*\r
4819        * refresh the tags dialog only if it's visible\r
4820        */\r
4821       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4822           char *tags;\r
4823           tags = PGNTags(&gameInfo);\r
4824           TagsPopUp(tags, CmailMsg());\r
4825           free(tags);\r
4826       }\r
4827       SAY("computer starts playing white");\r
4828       break;\r
4829 \r
4830     case IDM_MachineBlack:\r
4831       MachineBlackEvent();\r
4832       /*\r
4833        * refresh the tags dialog only if it's visible\r
4834        */\r
4835       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4836           char *tags;\r
4837           tags = PGNTags(&gameInfo);\r
4838           TagsPopUp(tags, CmailMsg());\r
4839           free(tags);\r
4840       }\r
4841       SAY("computer starts playing black");\r
4842       break;\r
4843 \r
4844     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4845       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4846       break;\r
4847 \r
4848     case IDM_TwoMachines:\r
4849       TwoMachinesEvent();\r
4850       /*\r
4851        * refresh the tags dialog only if it's visible\r
4852        */\r
4853       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4854           char *tags;\r
4855           tags = PGNTags(&gameInfo);\r
4856           TagsPopUp(tags, CmailMsg());\r
4857           free(tags);\r
4858       }\r
4859       SAY("computer starts playing both sides");\r
4860       break;\r
4861 \r
4862     case IDM_AnalysisMode:\r
4863       if (!first.analysisSupport) {\r
4864         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4865         DisplayError(buf, 0);\r
4866       } else {\r
4867         SAY("analyzing current position");\r
4868         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4869         if (appData.icsActive) {\r
4870                if (gameMode != IcsObserving) {\r
4871                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4872                        DisplayError(buf, 0);\r
4873                        /* secure check */\r
4874                        if (appData.icsEngineAnalyze) {\r
4875                                if (appData.debugMode) \r
4876                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4877                                ExitAnalyzeMode();\r
4878                                ModeHighlight();\r
4879                                break;\r
4880                        }\r
4881                        break;\r
4882                } else {\r
4883                        /* if enable, user want disable icsEngineAnalyze */\r
4884                        if (appData.icsEngineAnalyze) {\r
4885                                ExitAnalyzeMode();\r
4886                                ModeHighlight();\r
4887                                break;\r
4888                        }\r
4889                        appData.icsEngineAnalyze = TRUE;\r
4890                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4891                }\r
4892         } \r
4893         if (!appData.showThinking) ToggleShowThinking();\r
4894         AnalyzeModeEvent();\r
4895       }\r
4896       break;\r
4897 \r
4898     case IDM_AnalyzeFile:\r
4899       if (!first.analysisSupport) {\r
4900         char buf[MSG_SIZ];\r
4901           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4902         DisplayError(buf, 0);\r
4903       } else {\r
4904         if (!appData.showThinking) ToggleShowThinking();\r
4905         AnalyzeFileEvent();\r
4906         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4907         AnalysisPeriodicEvent(1);\r
4908       }\r
4909       break;\r
4910 \r
4911     case IDM_IcsClient:\r
4912       IcsClientEvent();\r
4913       break;\r
4914 \r
4915     case IDM_EditGame:\r
4916     case IDM_EditGame2:\r
4917       EditGameEvent();\r
4918       SAY("edit game");\r
4919       break;\r
4920 \r
4921     case IDM_EditPosition:\r
4922     case IDM_EditPosition2:\r
4923       EditPositionEvent();\r
4924       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4925       break;\r
4926 \r
4927     case IDM_Training:\r
4928       TrainingEvent();\r
4929       break;\r
4930 \r
4931     case IDM_ShowGameList:\r
4932       ShowGameListProc();\r
4933       break;\r
4934 \r
4935     case IDM_EditProgs1:\r
4936       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4937       break;\r
4938 \r
4939     case IDM_EditProgs2:\r
4940       EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4941       break;\r
4942 \r
4943     case IDM_EditServers:\r
4944       EditTagsPopUp(icsNames, &icsNames);\r
4945       break;\r
4946 \r
4947     case IDM_EditTags:\r
4948     case IDM_Tags:\r
4949       EditTagsProc();\r
4950       break;\r
4951 \r
4952     case IDM_EditComment:\r
4953     case IDM_Comment:\r
4954       if (commentUp && editComment) {\r
4955         CommentPopDown();\r
4956       } else {\r
4957         EditCommentEvent();\r
4958       }\r
4959       break;\r
4960 \r
4961     case IDM_Pause:\r
4962       PauseEvent();\r
4963       break;\r
4964 \r
4965     case IDM_Accept:\r
4966       AcceptEvent();\r
4967       break;\r
4968 \r
4969     case IDM_Decline:\r
4970       DeclineEvent();\r
4971       break;\r
4972 \r
4973     case IDM_Rematch:\r
4974       RematchEvent();\r
4975       break;\r
4976 \r
4977     case IDM_CallFlag:\r
4978       CallFlagEvent();\r
4979       break;\r
4980 \r
4981     case IDM_Draw:\r
4982       DrawEvent();\r
4983       break;\r
4984 \r
4985     case IDM_Adjourn:\r
4986       AdjournEvent();\r
4987       break;\r
4988 \r
4989     case IDM_Abort:\r
4990       AbortEvent();\r
4991       break;\r
4992 \r
4993     case IDM_Resign:\r
4994       ResignEvent();\r
4995       break;\r
4996 \r
4997     case IDM_StopObserving:\r
4998       StopObservingEvent();\r
4999       break;\r
5000 \r
5001     case IDM_StopExamining:\r
5002       StopExaminingEvent();\r
5003       break;\r
5004 \r
5005     case IDM_Upload:\r
5006       UploadGameEvent();\r
5007       break;\r
5008 \r
5009     case IDM_TypeInMove:\r
5010       TypeInEvent('\000');\r
5011       break;\r
5012 \r
5013     case IDM_TypeInName:\r
5014       PopUpNameDialog('\000');\r
5015       break;\r
5016 \r
5017     case IDM_Backward:\r
5018       BackwardEvent();\r
5019       SetFocus(hwndMain);\r
5020       break;\r
5021 \r
5022     JAWS_MENU_ITEMS\r
5023 \r
5024     case IDM_Forward:\r
5025       ForwardEvent();\r
5026       SetFocus(hwndMain);\r
5027       break;\r
5028 \r
5029     case IDM_ToStart:\r
5030       ToStartEvent();\r
5031       SetFocus(hwndMain);\r
5032       break;\r
5033 \r
5034     case IDM_ToEnd:\r
5035       ToEndEvent();\r
5036       SetFocus(hwndMain);\r
5037       break;\r
5038 \r
5039     case IDM_Revert:\r
5040       RevertEvent(FALSE);\r
5041       break;\r
5042 \r
5043     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5044       RevertEvent(TRUE);\r
5045       break;\r
5046 \r
5047     case IDM_TruncateGame:\r
5048       TruncateGameEvent();\r
5049       break;\r
5050 \r
5051     case IDM_MoveNow:\r
5052       MoveNowEvent();\r
5053       break;\r
5054 \r
5055     case IDM_RetractMove:\r
5056       RetractMoveEvent();\r
5057       break;\r
5058 \r
5059     case IDM_FlipView:\r
5060       flipView = !flipView;\r
5061       DrawPosition(FALSE, NULL);\r
5062       break;\r
5063 \r
5064     case IDM_FlipClock:\r
5065       flipClock = !flipClock;\r
5066       DisplayBothClocks();\r
5067       DisplayLogos();\r
5068       break;\r
5069 \r
5070     case IDM_MuteSounds:\r
5071       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5072       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5073                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5074       break;\r
5075 \r
5076     case IDM_GeneralOptions:\r
5077       GeneralOptionsPopup(hwnd);\r
5078       DrawPosition(TRUE, NULL);\r
5079       break;\r
5080 \r
5081     case IDM_BoardOptions:\r
5082       BoardOptionsPopup(hwnd);\r
5083       break;\r
5084 \r
5085     case IDM_EnginePlayOptions:\r
5086       EnginePlayOptionsPopup(hwnd);\r
5087       break;\r
5088 \r
5089     case IDM_Engine1Options:\r
5090       EngineOptionsPopup(hwnd, &first);\r
5091       break;\r
5092 \r
5093     case IDM_Engine2Options:\r
5094       savedHwnd = hwnd;\r
5095       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5096       EngineOptionsPopup(hwnd, &second);\r
5097       break;\r
5098 \r
5099     case IDM_OptionsUCI:\r
5100       UciOptionsPopup(hwnd);\r
5101       break;\r
5102 \r
5103     case IDM_IcsOptions:\r
5104       IcsOptionsPopup(hwnd);\r
5105       break;\r
5106 \r
5107     case IDM_Fonts:\r
5108       FontsOptionsPopup(hwnd);\r
5109       break;\r
5110 \r
5111     case IDM_Sounds:\r
5112       SoundOptionsPopup(hwnd);\r
5113       break;\r
5114 \r
5115     case IDM_CommPort:\r
5116       CommPortOptionsPopup(hwnd);\r
5117       break;\r
5118 \r
5119     case IDM_LoadOptions:\r
5120       LoadOptionsPopup(hwnd);\r
5121       break;\r
5122 \r
5123     case IDM_SaveOptions:\r
5124       SaveOptionsPopup(hwnd);\r
5125       break;\r
5126 \r
5127     case IDM_TimeControl:\r
5128       TimeControlOptionsPopup(hwnd);\r
5129       break;\r
5130 \r
5131     case IDM_SaveSettings:\r
5132       SaveSettings(settingsFileName);\r
5133       break;\r
5134 \r
5135     case IDM_SaveSettingsOnExit:\r
5136       saveSettingsOnExit = !saveSettingsOnExit;\r
5137       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5138                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5139                                          MF_CHECKED : MF_UNCHECKED));\r
5140       break;\r
5141 \r
5142     case IDM_Hint:\r
5143       HintEvent();\r
5144       break;\r
5145 \r
5146     case IDM_Book:\r
5147       BookEvent();\r
5148       break;\r
5149 \r
5150     case IDM_AboutGame:\r
5151       AboutGameEvent();\r
5152       break;\r
5153 \r
5154     case IDM_Debug:\r
5155       appData.debugMode = !appData.debugMode;\r
5156       if (appData.debugMode) {\r
5157         char dir[MSG_SIZ];\r
5158         GetCurrentDirectory(MSG_SIZ, dir);\r
5159         SetCurrentDirectory(installDir);\r
5160         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5161         SetCurrentDirectory(dir);\r
5162         setbuf(debugFP, NULL);\r
5163       } else {\r
5164         fclose(debugFP);\r
5165         debugFP = NULL;\r
5166       }\r
5167       break;\r
5168 \r
5169     case IDM_HELPCONTENTS:\r
5170       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5171           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5172           MessageBox (GetFocus(),\r
5173                     _("Unable to activate help"),\r
5174                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5175       }\r
5176       break;\r
5177 \r
5178     case IDM_HELPSEARCH:\r
5179         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5180             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5181         MessageBox (GetFocus(),\r
5182                     _("Unable to activate help"),\r
5183                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5184       }\r
5185       break;\r
5186 \r
5187     case IDM_HELPHELP:\r
5188       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5189         MessageBox (GetFocus(),\r
5190                     _("Unable to activate help"),\r
5191                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5192       }\r
5193       break;\r
5194 \r
5195     case IDM_ABOUT:\r
5196       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5197       DialogBox(hInst, \r
5198         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5199         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5200       FreeProcInstance(lpProc);\r
5201       break;\r
5202 \r
5203     case IDM_DirectCommand1:\r
5204       AskQuestionEvent(_("Direct Command"),\r
5205                        _("Send to chess program:"), "", "1");\r
5206       break;\r
5207     case IDM_DirectCommand2:\r
5208       AskQuestionEvent(_("Direct Command"),\r
5209                        _("Send to second chess program:"), "", "2");\r
5210       break;\r
5211 \r
5212     case EP_WhitePawn:\r
5213       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5214       fromX = fromY = -1;\r
5215       break;\r
5216 \r
5217     case EP_WhiteKnight:\r
5218       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5219       fromX = fromY = -1;\r
5220       break;\r
5221 \r
5222     case EP_WhiteBishop:\r
5223       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5224       fromX = fromY = -1;\r
5225       break;\r
5226 \r
5227     case EP_WhiteRook:\r
5228       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5229       fromX = fromY = -1;\r
5230       break;\r
5231 \r
5232     case EP_WhiteQueen:\r
5233       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5234       fromX = fromY = -1;\r
5235       break;\r
5236 \r
5237     case EP_WhiteFerz:\r
5238       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5239       fromX = fromY = -1;\r
5240       break;\r
5241 \r
5242     case EP_WhiteWazir:\r
5243       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5244       fromX = fromY = -1;\r
5245       break;\r
5246 \r
5247     case EP_WhiteAlfil:\r
5248       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5249       fromX = fromY = -1;\r
5250       break;\r
5251 \r
5252     case EP_WhiteCannon:\r
5253       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5254       fromX = fromY = -1;\r
5255       break;\r
5256 \r
5257     case EP_WhiteCardinal:\r
5258       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5259       fromX = fromY = -1;\r
5260       break;\r
5261 \r
5262     case EP_WhiteMarshall:\r
5263       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5264       fromX = fromY = -1;\r
5265       break;\r
5266 \r
5267     case EP_WhiteKing:\r
5268       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5269       fromX = fromY = -1;\r
5270       break;\r
5271 \r
5272     case EP_BlackPawn:\r
5273       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5274       fromX = fromY = -1;\r
5275       break;\r
5276 \r
5277     case EP_BlackKnight:\r
5278       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5279       fromX = fromY = -1;\r
5280       break;\r
5281 \r
5282     case EP_BlackBishop:\r
5283       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5284       fromX = fromY = -1;\r
5285       break;\r
5286 \r
5287     case EP_BlackRook:\r
5288       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5289       fromX = fromY = -1;\r
5290       break;\r
5291 \r
5292     case EP_BlackQueen:\r
5293       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5294       fromX = fromY = -1;\r
5295       break;\r
5296 \r
5297     case EP_BlackFerz:\r
5298       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5299       fromX = fromY = -1;\r
5300       break;\r
5301 \r
5302     case EP_BlackWazir:\r
5303       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5304       fromX = fromY = -1;\r
5305       break;\r
5306 \r
5307     case EP_BlackAlfil:\r
5308       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5309       fromX = fromY = -1;\r
5310       break;\r
5311 \r
5312     case EP_BlackCannon:\r
5313       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5314       fromX = fromY = -1;\r
5315       break;\r
5316 \r
5317     case EP_BlackCardinal:\r
5318       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5319       fromX = fromY = -1;\r
5320       break;\r
5321 \r
5322     case EP_BlackMarshall:\r
5323       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5324       fromX = fromY = -1;\r
5325       break;\r
5326 \r
5327     case EP_BlackKing:\r
5328       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5329       fromX = fromY = -1;\r
5330       break;\r
5331 \r
5332     case EP_EmptySquare:\r
5333       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5334       fromX = fromY = -1;\r
5335       break;\r
5336 \r
5337     case EP_ClearBoard:\r
5338       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5339       fromX = fromY = -1;\r
5340       break;\r
5341 \r
5342     case EP_White:\r
5343       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5344       fromX = fromY = -1;\r
5345       break;\r
5346 \r
5347     case EP_Black:\r
5348       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5349       fromX = fromY = -1;\r
5350       break;\r
5351 \r
5352     case EP_Promote:\r
5353       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5354       fromX = fromY = -1;\r
5355       break;\r
5356 \r
5357     case EP_Demote:\r
5358       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5359       fromX = fromY = -1;\r
5360       break;\r
5361 \r
5362     case DP_Pawn:\r
5363       DropMenuEvent(WhitePawn, fromX, fromY);\r
5364       fromX = fromY = -1;\r
5365       break;\r
5366 \r
5367     case DP_Knight:\r
5368       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5369       fromX = fromY = -1;\r
5370       break;\r
5371 \r
5372     case DP_Bishop:\r
5373       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5374       fromX = fromY = -1;\r
5375       break;\r
5376 \r
5377     case DP_Rook:\r
5378       DropMenuEvent(WhiteRook, fromX, fromY);\r
5379       fromX = fromY = -1;\r
5380       break;\r
5381 \r
5382     case DP_Queen:\r
5383       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5384       fromX = fromY = -1;\r
5385       break;\r
5386 \r
5387     case IDM_English:\r
5388       barbaric = 0; appData.language = "";\r
5389       TranslateMenus(0);\r
5390       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5391       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5392       lastChecked = wmId;\r
5393       break;\r
5394 \r
5395     default:\r
5396       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5397           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5398           TranslateMenus(0);\r
5399           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5400           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5401           lastChecked = wmId;\r
5402           break;\r
5403       }\r
5404       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5405     }\r
5406     break;\r
5407 \r
5408   case WM_TIMER:\r
5409     switch (wParam) {\r
5410     case CLOCK_TIMER_ID:\r
5411       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5412       clockTimerEvent = 0;\r
5413       DecrementClocks(); /* call into back end */\r
5414       break;\r
5415     case LOAD_GAME_TIMER_ID:\r
5416       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5417       loadGameTimerEvent = 0;\r
5418       AutoPlayGameLoop(); /* call into back end */\r
5419       break;\r
5420     case ANALYSIS_TIMER_ID:\r
5421       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5422                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5423         AnalysisPeriodicEvent(0);\r
5424       } else {\r
5425         KillTimer(hwnd, analysisTimerEvent);\r
5426         analysisTimerEvent = 0;\r
5427       }\r
5428       break;\r
5429     case DELAYED_TIMER_ID:\r
5430       KillTimer(hwnd, delayedTimerEvent);\r
5431       delayedTimerEvent = 0;\r
5432       delayedTimerCallback();\r
5433       break;\r
5434     }\r
5435     break;\r
5436 \r
5437   case WM_USER_Input:\r
5438     InputEvent(hwnd, message, wParam, lParam);\r
5439     break;\r
5440 \r
5441   /* [AS] Also move "attached" child windows */\r
5442   case WM_WINDOWPOSCHANGING:\r
5443 \r
5444     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5445         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5446 \r
5447         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5448             /* Window is moving */\r
5449             RECT rcMain;\r
5450 \r
5451 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5452             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5453             rcMain.right  = wpMain.x + wpMain.width;\r
5454             rcMain.top    = wpMain.y;\r
5455             rcMain.bottom = wpMain.y + wpMain.height;\r
5456             \r
5457             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5458             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5459             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5460             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5461             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5462             wpMain.x = lpwp->x;\r
5463             wpMain.y = lpwp->y;\r
5464         }\r
5465     }\r
5466     break;\r
5467 \r
5468   /* [AS] Snapping */\r
5469   case WM_ENTERSIZEMOVE:\r
5470     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5471     if (hwnd == hwndMain) {\r
5472       doingSizing = TRUE;\r
5473       lastSizing = 0;\r
5474     }\r
5475     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5476     break;\r
5477 \r
5478   case WM_SIZING:\r
5479     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5480     if (hwnd == hwndMain) {\r
5481       lastSizing = wParam;\r
5482     }\r
5483     break;\r
5484 \r
5485   case WM_MOVING:\r
5486     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5487       return OnMoving( &sd, hwnd, wParam, lParam );\r
5488 \r
5489   case WM_EXITSIZEMOVE:\r
5490     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5491     if (hwnd == hwndMain) {\r
5492       RECT client;\r
5493       doingSizing = FALSE;\r
5494       InvalidateRect(hwnd, &boardRect, FALSE);\r
5495       GetClientRect(hwnd, &client);\r
5496       ResizeBoard(client.right, client.bottom, lastSizing);\r
5497       lastSizing = 0;\r
5498       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5499     }\r
5500     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5501     break;\r
5502 \r
5503   case WM_DESTROY: /* message: window being destroyed */\r
5504     PostQuitMessage(0);\r
5505     break;\r
5506 \r
5507   case WM_CLOSE:\r
5508     if (hwnd == hwndMain) {\r
5509       ExitEvent(0);\r
5510     }\r
5511     break;\r
5512 \r
5513   default:      /* Passes it on if unprocessed */\r
5514     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5515   }\r
5516   return 0;\r
5517 }\r
5518 \r
5519 /*---------------------------------------------------------------------------*\\r
5520  *\r
5521  * Misc utility routines\r
5522  *\r
5523 \*---------------------------------------------------------------------------*/\r
5524 \r
5525 /*\r
5526  * Decent random number generator, at least not as bad as Windows\r
5527  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5528  */\r
5529 unsigned int randstate;\r
5530 \r
5531 int\r
5532 myrandom(void)\r
5533 {\r
5534   randstate = randstate * 1664525 + 1013904223;\r
5535   return (int) randstate & 0x7fffffff;\r
5536 }\r
5537 \r
5538 void\r
5539 mysrandom(unsigned int seed)\r
5540 {\r
5541   randstate = seed;\r
5542 }\r
5543 \r
5544 \r
5545 /* \r
5546  * returns TRUE if user selects a different color, FALSE otherwise \r
5547  */\r
5548 \r
5549 BOOL\r
5550 ChangeColor(HWND hwnd, COLORREF *which)\r
5551 {\r
5552   static BOOL firstTime = TRUE;\r
5553   static DWORD customColors[16];\r
5554   CHOOSECOLOR cc;\r
5555   COLORREF newcolor;\r
5556   int i;\r
5557   ColorClass ccl;\r
5558 \r
5559   if (firstTime) {\r
5560     /* Make initial colors in use available as custom colors */\r
5561     /* Should we put the compiled-in defaults here instead? */\r
5562     i = 0;\r
5563     customColors[i++] = lightSquareColor & 0xffffff;\r
5564     customColors[i++] = darkSquareColor & 0xffffff;\r
5565     customColors[i++] = whitePieceColor & 0xffffff;\r
5566     customColors[i++] = blackPieceColor & 0xffffff;\r
5567     customColors[i++] = highlightSquareColor & 0xffffff;\r
5568     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5569 \r
5570     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5571       customColors[i++] = textAttribs[ccl].color;\r
5572     }\r
5573     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5574     firstTime = FALSE;\r
5575   }\r
5576 \r
5577   cc.lStructSize = sizeof(cc);\r
5578   cc.hwndOwner = hwnd;\r
5579   cc.hInstance = NULL;\r
5580   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5581   cc.lpCustColors = (LPDWORD) customColors;\r
5582   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5583 \r
5584   if (!ChooseColor(&cc)) return FALSE;\r
5585 \r
5586   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5587   if (newcolor == *which) return FALSE;\r
5588   *which = newcolor;\r
5589   return TRUE;\r
5590 \r
5591   /*\r
5592   InitDrawingColors();\r
5593   InvalidateRect(hwnd, &boardRect, FALSE);\r
5594   */\r
5595 }\r
5596 \r
5597 BOOLEAN\r
5598 MyLoadSound(MySound *ms)\r
5599 {\r
5600   BOOL ok = FALSE;\r
5601   struct stat st;\r
5602   FILE *f;\r
5603 \r
5604   if (ms->data) free(ms->data);\r
5605   ms->data = NULL;\r
5606 \r
5607   switch (ms->name[0]) {\r
5608   case NULLCHAR:\r
5609     /* Silence */\r
5610     ok = TRUE;\r
5611     break;\r
5612   case '$':\r
5613     /* System sound from Control Panel.  Don't preload here. */\r
5614     ok = TRUE;\r
5615     break;\r
5616   case '!':\r
5617     if (ms->name[1] == NULLCHAR) {\r
5618       /* "!" alone = silence */\r
5619       ok = TRUE;\r
5620     } else {\r
5621       /* Builtin wave resource.  Error if not found. */\r
5622       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5623       if (h == NULL) break;\r
5624       ms->data = (void *)LoadResource(hInst, h);\r
5625       if (h == NULL) break;\r
5626       ok = TRUE;\r
5627     }\r
5628     break;\r
5629   default:\r
5630     /* .wav file.  Error if not found. */\r
5631     f = fopen(ms->name, "rb");\r
5632     if (f == NULL) break;\r
5633     if (fstat(fileno(f), &st) < 0) break;\r
5634     ms->data = malloc(st.st_size);\r
5635     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5636     fclose(f);\r
5637     ok = TRUE;\r
5638     break;\r
5639   }\r
5640   if (!ok) {\r
5641     char buf[MSG_SIZ];\r
5642       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5643     DisplayError(buf, GetLastError());\r
5644   }\r
5645   return ok;\r
5646 }\r
5647 \r
5648 BOOLEAN\r
5649 MyPlaySound(MySound *ms)\r
5650 {\r
5651   BOOLEAN ok = FALSE;\r
5652 \r
5653   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5654   switch (ms->name[0]) {\r
5655   case NULLCHAR:\r
5656         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5657     /* Silence */\r
5658     ok = TRUE;\r
5659     break;\r
5660   case '$':\r
5661     /* System sound from Control Panel (deprecated feature).\r
5662        "$" alone or an unset sound name gets default beep (still in use). */\r
5663     if (ms->name[1]) {\r
5664       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5665     }\r
5666     if (!ok) ok = MessageBeep(MB_OK);\r
5667     break; \r
5668   case '!':\r
5669     /* Builtin wave resource, or "!" alone for silence */\r
5670     if (ms->name[1]) {\r
5671       if (ms->data == NULL) return FALSE;\r
5672       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5673     } else {\r
5674       ok = TRUE;\r
5675     }\r
5676     break;\r
5677   default:\r
5678     /* .wav file.  Error if not found. */\r
5679     if (ms->data == NULL) return FALSE;\r
5680     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5681     break;\r
5682   }\r
5683   /* Don't print an error: this can happen innocently if the sound driver\r
5684      is busy; for instance, if another instance of WinBoard is playing\r
5685      a sound at about the same time. */\r
5686   return ok;\r
5687 }\r
5688 \r
5689 \r
5690 LRESULT CALLBACK\r
5691 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5692 {\r
5693   BOOL ok;\r
5694   OPENFILENAME *ofn;\r
5695   static UINT *number; /* gross that this is static */\r
5696 \r
5697   switch (message) {\r
5698   case WM_INITDIALOG: /* message: initialize dialog box */\r
5699     /* Center the dialog over the application window */\r
5700     ofn = (OPENFILENAME *) lParam;\r
5701     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5702       number = (UINT *) ofn->lCustData;\r
5703       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5704     } else {\r
5705       number = NULL;\r
5706     }\r
5707     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5708     Translate(hDlg, 1536);\r
5709     return FALSE;  /* Allow for further processing */\r
5710 \r
5711   case WM_COMMAND:\r
5712     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5713       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5714     }\r
5715     return FALSE;  /* Allow for further processing */\r
5716   }\r
5717   return FALSE;\r
5718 }\r
5719 \r
5720 UINT APIENTRY\r
5721 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5722 {\r
5723   static UINT *number;\r
5724   OPENFILENAME *ofname;\r
5725   OFNOTIFY *ofnot;\r
5726   switch (uiMsg) {\r
5727   case WM_INITDIALOG:\r
5728     Translate(hdlg, DLG_IndexNumber);\r
5729     ofname = (OPENFILENAME *)lParam;\r
5730     number = (UINT *)(ofname->lCustData);\r
5731     break;\r
5732   case WM_NOTIFY:\r
5733     ofnot = (OFNOTIFY *)lParam;\r
5734     if (ofnot->hdr.code == CDN_FILEOK) {\r
5735       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5736     }\r
5737     break;\r
5738   }\r
5739   return 0;\r
5740 }\r
5741 \r
5742 \r
5743 FILE *\r
5744 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5745                char *nameFilt, char *dlgTitle, UINT *number,\r
5746                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5747 {\r
5748   OPENFILENAME openFileName;\r
5749   char buf1[MSG_SIZ];\r
5750   FILE *f;\r
5751 \r
5752   if (fileName == NULL) fileName = buf1;\r
5753   if (defName == NULL) {\r
5754     safeStrCpy(fileName, "*.", 3 );\r
5755     strcat(fileName, defExt);\r
5756   } else {\r
5757     safeStrCpy(fileName, defName, MSG_SIZ );\r
5758   }\r
5759     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5760   if (number) *number = 0;\r
5761 \r
5762   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5763   openFileName.hwndOwner         = hwnd;\r
5764   openFileName.hInstance         = (HANDLE) hInst;\r
5765   openFileName.lpstrFilter       = nameFilt;\r
5766   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5767   openFileName.nMaxCustFilter    = 0L;\r
5768   openFileName.nFilterIndex      = 1L;\r
5769   openFileName.lpstrFile         = fileName;\r
5770   openFileName.nMaxFile          = MSG_SIZ;\r
5771   openFileName.lpstrFileTitle    = fileTitle;\r
5772   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5773   openFileName.lpstrInitialDir   = NULL;\r
5774   openFileName.lpstrTitle        = dlgTitle;\r
5775   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5776     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5777     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5778     | (oldDialog ? 0 : OFN_EXPLORER);\r
5779   openFileName.nFileOffset       = 0;\r
5780   openFileName.nFileExtension    = 0;\r
5781   openFileName.lpstrDefExt       = defExt;\r
5782   openFileName.lCustData         = (LONG) number;\r
5783   openFileName.lpfnHook          = oldDialog ?\r
5784     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5785   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5786 \r
5787   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5788                         GetOpenFileName(&openFileName)) {\r
5789     /* open the file */\r
5790     f = fopen(openFileName.lpstrFile, write);\r
5791     if (f == NULL) {\r
5792       MessageBox(hwnd, _("File open failed"), NULL,\r
5793                  MB_OK|MB_ICONEXCLAMATION);\r
5794       return NULL;\r
5795     }\r
5796   } else {\r
5797     int err = CommDlgExtendedError();\r
5798     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5799     return FALSE;\r
5800   }\r
5801   return f;\r
5802 }\r
5803 \r
5804 \r
5805 \r
5806 VOID APIENTRY\r
5807 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5808 {\r
5809   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5810 \r
5811   /*\r
5812    * Get the first pop-up menu in the menu template. This is the\r
5813    * menu that TrackPopupMenu displays.\r
5814    */\r
5815   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5816   TranslateOneMenu(10, hmenuTrackPopup);\r
5817 \r
5818   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5819 \r
5820   /*\r
5821    * TrackPopup uses screen coordinates, so convert the\r
5822    * coordinates of the mouse click to screen coordinates.\r
5823    */\r
5824   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5825 \r
5826   /* Draw and track the floating pop-up menu. */\r
5827   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5828                  pt.x, pt.y, 0, hwnd, NULL);\r
5829 \r
5830   /* Destroy the menu.*/\r
5831   DestroyMenu(hmenu);\r
5832 }\r
5833    \r
5834 typedef struct {\r
5835   HWND hDlg, hText;\r
5836   int sizeX, sizeY, newSizeX, newSizeY;\r
5837   HDWP hdwp;\r
5838 } ResizeEditPlusButtonsClosure;\r
5839 \r
5840 BOOL CALLBACK\r
5841 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5842 {\r
5843   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5844   RECT rect;\r
5845   POINT pt;\r
5846 \r
5847   if (hChild == cl->hText) return TRUE;\r
5848   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5849   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5850   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5851   ScreenToClient(cl->hDlg, &pt);\r
5852   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5853     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5854   return TRUE;\r
5855 }\r
5856 \r
5857 /* Resize a dialog that has a (rich) edit field filling most of\r
5858    the top, with a row of buttons below */\r
5859 VOID\r
5860 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5861 {\r
5862   RECT rectText;\r
5863   int newTextHeight, newTextWidth;\r
5864   ResizeEditPlusButtonsClosure cl;\r
5865   \r
5866   /*if (IsIconic(hDlg)) return;*/\r
5867   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5868   \r
5869   cl.hdwp = BeginDeferWindowPos(8);\r
5870 \r
5871   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5872   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5873   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5874   if (newTextHeight < 0) {\r
5875     newSizeY += -newTextHeight;\r
5876     newTextHeight = 0;\r
5877   }\r
5878   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5879     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5880 \r
5881   cl.hDlg = hDlg;\r
5882   cl.hText = hText;\r
5883   cl.sizeX = sizeX;\r
5884   cl.sizeY = sizeY;\r
5885   cl.newSizeX = newSizeX;\r
5886   cl.newSizeY = newSizeY;\r
5887   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5888 \r
5889   EndDeferWindowPos(cl.hdwp);\r
5890 }\r
5891 \r
5892 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5893 {\r
5894     RECT    rChild, rParent;\r
5895     int     wChild, hChild, wParent, hParent;\r
5896     int     wScreen, hScreen, xNew, yNew;\r
5897     HDC     hdc;\r
5898 \r
5899     /* Get the Height and Width of the child window */\r
5900     GetWindowRect (hwndChild, &rChild);\r
5901     wChild = rChild.right - rChild.left;\r
5902     hChild = rChild.bottom - rChild.top;\r
5903 \r
5904     /* Get the Height and Width of the parent window */\r
5905     GetWindowRect (hwndParent, &rParent);\r
5906     wParent = rParent.right - rParent.left;\r
5907     hParent = rParent.bottom - rParent.top;\r
5908 \r
5909     /* Get the display limits */\r
5910     hdc = GetDC (hwndChild);\r
5911     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5912     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5913     ReleaseDC(hwndChild, hdc);\r
5914 \r
5915     /* Calculate new X position, then adjust for screen */\r
5916     xNew = rParent.left + ((wParent - wChild) /2);\r
5917     if (xNew < 0) {\r
5918         xNew = 0;\r
5919     } else if ((xNew+wChild) > wScreen) {\r
5920         xNew = wScreen - wChild;\r
5921     }\r
5922 \r
5923     /* Calculate new Y position, then adjust for screen */\r
5924     if( mode == 0 ) {\r
5925         yNew = rParent.top  + ((hParent - hChild) /2);\r
5926     }\r
5927     else {\r
5928         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5929     }\r
5930 \r
5931     if (yNew < 0) {\r
5932         yNew = 0;\r
5933     } else if ((yNew+hChild) > hScreen) {\r
5934         yNew = hScreen - hChild;\r
5935     }\r
5936 \r
5937     /* Set it, and return */\r
5938     return SetWindowPos (hwndChild, NULL,\r
5939                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5940 }\r
5941 \r
5942 /* Center one window over another */\r
5943 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5944 {\r
5945     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5946 }\r
5947 \r
5948 /*---------------------------------------------------------------------------*\\r
5949  *\r
5950  * Startup Dialog functions\r
5951  *\r
5952 \*---------------------------------------------------------------------------*/\r
5953 void\r
5954 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5955 {\r
5956   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5957 \r
5958   while (*cd != NULL) {\r
5959     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5960     cd++;\r
5961   }\r
5962 }\r
5963 \r
5964 void\r
5965 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5966 {\r
5967   char buf1[MAX_ARG_LEN];\r
5968   int len;\r
5969 \r
5970   if (str[0] == '@') {\r
5971     FILE* f = fopen(str + 1, "r");\r
5972     if (f == NULL) {\r
5973       DisplayFatalError(str + 1, errno, 2);\r
5974       return;\r
5975     }\r
5976     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5977     fclose(f);\r
5978     buf1[len] = NULLCHAR;\r
5979     str = buf1;\r
5980   }\r
5981 \r
5982   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5983 \r
5984   for (;;) {\r
5985     char buf[MSG_SIZ];\r
5986     char *end = strchr(str, '\n');\r
5987     if (end == NULL) return;\r
5988     memcpy(buf, str, end - str);\r
5989     buf[end - str] = NULLCHAR;\r
5990     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5991     str = end + 1;\r
5992   }\r
5993 }\r
5994 \r
5995 void\r
5996 SetStartupDialogEnables(HWND hDlg)\r
5997 {\r
5998   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5999     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6000     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6001   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6002     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6003   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6004     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6005   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6006     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6007   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6008     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6009     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6010     IsDlgButtonChecked(hDlg, OPT_View));\r
6011 }\r
6012 \r
6013 char *\r
6014 QuoteForFilename(char *filename)\r
6015 {\r
6016   int dquote, space;\r
6017   dquote = strchr(filename, '"') != NULL;\r
6018   space = strchr(filename, ' ') != NULL;\r
6019   if (dquote || space) {\r
6020     if (dquote) {\r
6021       return "'";\r
6022     } else {\r
6023       return "\"";\r
6024     }\r
6025   } else {\r
6026     return "";\r
6027   }\r
6028 }\r
6029 \r
6030 VOID\r
6031 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6032 {\r
6033   char buf[MSG_SIZ];\r
6034   char *q;\r
6035 \r
6036   InitComboStringsFromOption(hwndCombo, nthnames);\r
6037   q = QuoteForFilename(nthcp);\r
6038     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6039   if (*nthdir != NULLCHAR) {\r
6040     q = QuoteForFilename(nthdir);\r
6041       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6042   }\r
6043   if (*nthcp == NULLCHAR) {\r
6044     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6045   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6046     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6047     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6048   }\r
6049 }\r
6050 \r
6051 LRESULT CALLBACK\r
6052 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6053 {\r
6054   char buf[MSG_SIZ];\r
6055   HANDLE hwndCombo;\r
6056   char *p;\r
6057 \r
6058   switch (message) {\r
6059   case WM_INITDIALOG:\r
6060     /* Center the dialog */\r
6061     CenterWindow (hDlg, GetDesktopWindow());\r
6062     Translate(hDlg, DLG_Startup);\r
6063     /* Initialize the dialog items */\r
6064     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6065                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6066                   firstChessProgramNames);\r
6067     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6068                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6069                   secondChessProgramNames);\r
6070     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6071     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6072       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6073     if (*appData.icsHelper != NULLCHAR) {\r
6074       char *q = QuoteForFilename(appData.icsHelper);\r
6075       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6076     }\r
6077     if (*appData.icsHost == NULLCHAR) {\r
6078       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6079       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6080     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6081       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6082       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6083     }\r
6084 \r
6085     if (appData.icsActive) {\r
6086       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6087     }\r
6088     else if (appData.noChessProgram) {\r
6089       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6090     }\r
6091     else {\r
6092       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6093     }\r
6094 \r
6095     SetStartupDialogEnables(hDlg);\r
6096     return TRUE;\r
6097 \r
6098   case WM_COMMAND:\r
6099     switch (LOWORD(wParam)) {\r
6100     case IDOK:\r
6101       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6102         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6103         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6104         p = buf;\r
6105         ParseArgs(StringGet, &p);\r
6106         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6107         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6108         p = buf;\r
6109         ParseArgs(StringGet, &p);\r
6110         appData.noChessProgram = FALSE;\r
6111         appData.icsActive = FALSE;\r
6112       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6113         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6114         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6115         p = buf;\r
6116         ParseArgs(StringGet, &p);\r
6117         if (appData.zippyPlay) {\r
6118           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6119           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6120           p = buf;\r
6121           ParseArgs(StringGet, &p);\r
6122         }\r
6123       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6124         appData.noChessProgram = TRUE;\r
6125         appData.icsActive = FALSE;\r
6126       } else {\r
6127         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6128                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6129         return TRUE;\r
6130       }\r
6131       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6132         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6133         p = buf;\r
6134         ParseArgs(StringGet, &p);\r
6135       }\r
6136       EndDialog(hDlg, TRUE);\r
6137       return TRUE;\r
6138 \r
6139     case IDCANCEL:\r
6140       ExitEvent(0);\r
6141       return TRUE;\r
6142 \r
6143     case IDM_HELPCONTENTS:\r
6144       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6145         MessageBox (GetFocus(),\r
6146                     _("Unable to activate help"),\r
6147                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6148       }\r
6149       break;\r
6150 \r
6151     default:\r
6152       SetStartupDialogEnables(hDlg);\r
6153       break;\r
6154     }\r
6155     break;\r
6156   }\r
6157   return FALSE;\r
6158 }\r
6159 \r
6160 /*---------------------------------------------------------------------------*\\r
6161  *\r
6162  * About box dialog functions\r
6163  *\r
6164 \*---------------------------------------------------------------------------*/\r
6165 \r
6166 /* Process messages for "About" dialog box */\r
6167 LRESULT CALLBACK\r
6168 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6169 {\r
6170   switch (message) {\r
6171   case WM_INITDIALOG: /* message: initialize dialog box */\r
6172     /* Center the dialog over the application window */\r
6173     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6174     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6175     Translate(hDlg, ABOUTBOX);\r
6176     JAWS_COPYRIGHT\r
6177     return (TRUE);\r
6178 \r
6179   case WM_COMMAND: /* message: received a command */\r
6180     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6181         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6182       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6183       return (TRUE);\r
6184     }\r
6185     break;\r
6186   }\r
6187   return (FALSE);\r
6188 }\r
6189 \r
6190 /*---------------------------------------------------------------------------*\\r
6191  *\r
6192  * Comment Dialog functions\r
6193  *\r
6194 \*---------------------------------------------------------------------------*/\r
6195 \r
6196 LRESULT CALLBACK\r
6197 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6198 {\r
6199   static HANDLE hwndText = NULL;\r
6200   int len, newSizeX, newSizeY, flags;\r
6201   static int sizeX, sizeY;\r
6202   char *str;\r
6203   RECT rect;\r
6204   MINMAXINFO *mmi;\r
6205 \r
6206   switch (message) {\r
6207   case WM_INITDIALOG: /* message: initialize dialog box */\r
6208     /* Initialize the dialog items */\r
6209     Translate(hDlg, DLG_EditComment);\r
6210     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6211     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6212     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6213     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6214     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6215     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6216     SetWindowText(hDlg, commentTitle);\r
6217     if (editComment) {\r
6218       SetFocus(hwndText);\r
6219     } else {\r
6220       SetFocus(GetDlgItem(hDlg, IDOK));\r
6221     }\r
6222     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6223                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6224                 MAKELPARAM(FALSE, 0));\r
6225     /* Size and position the dialog */\r
6226     if (!commentDialog) {\r
6227       commentDialog = hDlg;\r
6228       flags = SWP_NOZORDER;\r
6229       GetClientRect(hDlg, &rect);\r
6230       sizeX = rect.right;\r
6231       sizeY = rect.bottom;\r
6232       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6233           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6234         WINDOWPLACEMENT wp;\r
6235         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6236         wp.length = sizeof(WINDOWPLACEMENT);\r
6237         wp.flags = 0;\r
6238         wp.showCmd = SW_SHOW;\r
6239         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6240         wp.rcNormalPosition.left = wpComment.x;\r
6241         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6242         wp.rcNormalPosition.top = wpComment.y;\r
6243         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6244         SetWindowPlacement(hDlg, &wp);\r
6245 \r
6246         GetClientRect(hDlg, &rect);\r
6247         newSizeX = rect.right;\r
6248         newSizeY = rect.bottom;\r
6249         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6250                               newSizeX, newSizeY);\r
6251         sizeX = newSizeX;\r
6252         sizeY = newSizeY;\r
6253       }\r
6254     }\r
6255     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6256     return FALSE;\r
6257 \r
6258   case WM_COMMAND: /* message: received a command */\r
6259     switch (LOWORD(wParam)) {\r
6260     case IDOK:\r
6261       if (editComment) {\r
6262         char *p, *q;\r
6263         /* Read changed options from the dialog box */\r
6264         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6265         len = GetWindowTextLength(hwndText);\r
6266         str = (char *) malloc(len + 1);\r
6267         GetWindowText(hwndText, str, len + 1);\r
6268         p = q = str;\r
6269         while (*q) {\r
6270           if (*q == '\r')\r
6271             q++;\r
6272           else\r
6273             *p++ = *q++;\r
6274         }\r
6275         *p = NULLCHAR;\r
6276         ReplaceComment(commentIndex, str);\r
6277         free(str);\r
6278       }\r
6279       CommentPopDown();\r
6280       return TRUE;\r
6281 \r
6282     case IDCANCEL:\r
6283     case OPT_CancelComment:\r
6284       CommentPopDown();\r
6285       return TRUE;\r
6286 \r
6287     case OPT_ClearComment:\r
6288       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6289       break;\r
6290 \r
6291     case OPT_EditComment:\r
6292       EditCommentEvent();\r
6293       return TRUE;\r
6294 \r
6295     default:\r
6296       break;\r
6297     }\r
6298     break;\r
6299 \r
6300   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6301         if( wParam == OPT_CommentText ) {\r
6302             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6303 \r
6304             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6305                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6306                 POINTL pt;\r
6307                 LRESULT index;\r
6308 \r
6309                 pt.x = LOWORD( lpMF->lParam );\r
6310                 pt.y = HIWORD( lpMF->lParam );\r
6311 \r
6312                 if(lpMF->msg == WM_CHAR) {\r
6313                         CHARRANGE sel;\r
6314                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6315                         index = sel.cpMin;\r
6316                 } else\r
6317                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6318 \r
6319                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6320                 len = GetWindowTextLength(hwndText);\r
6321                 str = (char *) malloc(len + 1);\r
6322                 GetWindowText(hwndText, str, len + 1);\r
6323                 ReplaceComment(commentIndex, str);\r
6324                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6325                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6326                 free(str);\r
6327 \r
6328                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6329                 lpMF->msg = WM_USER;\r
6330 \r
6331                 return TRUE;\r
6332             }\r
6333         }\r
6334         break;\r
6335 \r
6336   case WM_SIZE:\r
6337     newSizeX = LOWORD(lParam);\r
6338     newSizeY = HIWORD(lParam);\r
6339     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6340     sizeX = newSizeX;\r
6341     sizeY = newSizeY;\r
6342     break;\r
6343 \r
6344   case WM_GETMINMAXINFO:\r
6345     /* Prevent resizing window too small */\r
6346     mmi = (MINMAXINFO *) lParam;\r
6347     mmi->ptMinTrackSize.x = 100;\r
6348     mmi->ptMinTrackSize.y = 100;\r
6349     break;\r
6350   }\r
6351   return FALSE;\r
6352 }\r
6353 \r
6354 VOID\r
6355 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6356 {\r
6357   FARPROC lpProc;\r
6358   char *p, *q;\r
6359 \r
6360   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6361 \r
6362   if (str == NULL) str = "";\r
6363   p = (char *) malloc(2 * strlen(str) + 2);\r
6364   q = p;\r
6365   while (*str) {\r
6366     if (*str == '\n') *q++ = '\r';\r
6367     *q++ = *str++;\r
6368   }\r
6369   *q = NULLCHAR;\r
6370   if (commentText != NULL) free(commentText);\r
6371 \r
6372   commentIndex = index;\r
6373   commentTitle = title;\r
6374   commentText = p;\r
6375   editComment = edit;\r
6376 \r
6377   if (commentDialog) {\r
6378     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6379     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6380   } else {\r
6381     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6382     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6383                  hwndMain, (DLGPROC)lpProc);\r
6384     FreeProcInstance(lpProc);\r
6385   }\r
6386   commentUp = TRUE;\r
6387 }\r
6388 \r
6389 \r
6390 /*---------------------------------------------------------------------------*\\r
6391  *\r
6392  * Type-in move dialog functions\r
6393  * \r
6394 \*---------------------------------------------------------------------------*/\r
6395 \r
6396 LRESULT CALLBACK\r
6397 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6398 {\r
6399   char move[MSG_SIZ];\r
6400   HWND hInput;\r
6401 \r
6402   switch (message) {\r
6403   case WM_INITDIALOG:\r
6404     move[0] = (char) lParam;\r
6405     move[1] = NULLCHAR;\r
6406     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6407     Translate(hDlg, DLG_TypeInMove);\r
6408     hInput = GetDlgItem(hDlg, OPT_Move);\r
6409     SetWindowText(hInput, move);\r
6410     SetFocus(hInput);\r
6411     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6412     return FALSE;\r
6413 \r
6414   case WM_COMMAND:\r
6415     switch (LOWORD(wParam)) {\r
6416     case IDOK:
6417 \r
6418       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6419       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6420       TypeInDoneEvent(move);\r
6421       EndDialog(hDlg, TRUE);\r
6422       return TRUE;\r
6423     case IDCANCEL:\r
6424       EndDialog(hDlg, FALSE);\r
6425       return TRUE;\r
6426     default:\r
6427       break;\r
6428     }\r
6429     break;\r
6430   }\r
6431   return FALSE;\r
6432 }\r
6433 \r
6434 VOID\r
6435 PopUpMoveDialog(char firstchar)\r
6436 {\r
6437     FARPROC lpProc;\r
6438 \r
6439       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6440       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6441         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6442       FreeProcInstance(lpProc);\r
6443 }\r
6444 \r
6445 /*---------------------------------------------------------------------------*\\r
6446  *\r
6447  * Type-in name dialog functions\r
6448  * \r
6449 \*---------------------------------------------------------------------------*/\r
6450 \r
6451 LRESULT CALLBACK\r
6452 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6453 {\r
6454   char move[MSG_SIZ];\r
6455   HWND hInput;\r
6456 \r
6457   switch (message) {\r
6458   case WM_INITDIALOG:\r
6459     move[0] = (char) lParam;\r
6460     move[1] = NULLCHAR;\r
6461     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6462     Translate(hDlg, DLG_TypeInName);\r
6463     hInput = GetDlgItem(hDlg, OPT_Name);\r
6464     SetWindowText(hInput, move);\r
6465     SetFocus(hInput);\r
6466     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6467     return FALSE;\r
6468 \r
6469   case WM_COMMAND:\r
6470     switch (LOWORD(wParam)) {\r
6471     case IDOK:\r
6472       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6473       appData.userName = strdup(move);\r
6474       SetUserLogo();\r
6475       SetGameInfo();\r
6476       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6477         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6478         DisplayTitle(move);\r
6479       }\r
6480 \r
6481 \r
6482       EndDialog(hDlg, TRUE);\r
6483       return TRUE;\r
6484     case IDCANCEL:\r
6485       EndDialog(hDlg, FALSE);\r
6486       return TRUE;\r
6487     default:\r
6488       break;\r
6489     }\r
6490     break;\r
6491   }\r
6492   return FALSE;\r
6493 }\r
6494 \r
6495 VOID\r
6496 PopUpNameDialog(char firstchar)\r
6497 {\r
6498     FARPROC lpProc;\r
6499     \r
6500       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6501       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6502         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6503       FreeProcInstance(lpProc);\r
6504 }\r
6505 \r
6506 /*---------------------------------------------------------------------------*\\r
6507  *\r
6508  *  Error dialogs\r
6509  * \r
6510 \*---------------------------------------------------------------------------*/\r
6511 \r
6512 /* Nonmodal error box */\r
6513 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6514                              WPARAM wParam, LPARAM lParam);\r
6515 \r
6516 VOID\r
6517 ErrorPopUp(char *title, char *content)\r
6518 {\r
6519   FARPROC lpProc;\r
6520   char *p, *q;\r
6521   BOOLEAN modal = hwndMain == NULL;\r
6522 \r
6523   p = content;\r
6524   q = errorMessage;\r
6525   while (*p) {\r
6526     if (*p == '\n') {\r
6527       if (modal) {\r
6528         *q++ = ' ';\r
6529         p++;\r
6530       } else {\r
6531         *q++ = '\r';\r
6532         *q++ = *p++;\r
6533       }\r
6534     } else {\r
6535       *q++ = *p++;\r
6536     }\r
6537   }\r
6538   *q = NULLCHAR;\r
6539   strncpy(errorTitle, title, sizeof(errorTitle));\r
6540   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6541   \r
6542   if (modal) {\r
6543     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6544   } else {\r
6545     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6546     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6547                  hwndMain, (DLGPROC)lpProc);\r
6548     FreeProcInstance(lpProc);\r
6549   }\r
6550 }\r
6551 \r
6552 VOID\r
6553 ErrorPopDown()\r
6554 {\r
6555   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6556   if (errorDialog == NULL) return;\r
6557   DestroyWindow(errorDialog);\r
6558   errorDialog = NULL;\r
6559   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6560 }\r
6561 \r
6562 LRESULT CALLBACK\r
6563 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6564 {\r
6565   HANDLE hwndText;\r
6566   RECT rChild;\r
6567 \r
6568   switch (message) {\r
6569   case WM_INITDIALOG:\r
6570     GetWindowRect(hDlg, &rChild);\r
6571 \r
6572     /*\r
6573     SetWindowPos(hDlg, NULL, rChild.left,\r
6574       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6575       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6576     */\r
6577 \r
6578     /* \r
6579         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6580         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6581         and it doesn't work when you resize the dialog.\r
6582         For now, just give it a default position.\r
6583     */\r
6584     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6585     Translate(hDlg, DLG_Error);\r
6586 \r
6587     errorDialog = hDlg;\r
6588     SetWindowText(hDlg, errorTitle);\r
6589     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6590     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6591     return FALSE;\r
6592 \r
6593   case WM_COMMAND:\r
6594     switch (LOWORD(wParam)) {\r
6595     case IDOK:\r
6596     case IDCANCEL:\r
6597       if (errorDialog == hDlg) errorDialog = NULL;\r
6598       DestroyWindow(hDlg);\r
6599       return TRUE;\r
6600 \r
6601     default:\r
6602       break;\r
6603     }\r
6604     break;\r
6605   }\r
6606   return FALSE;\r
6607 }\r
6608 \r
6609 #ifdef GOTHIC\r
6610 HWND gothicDialog = NULL;\r
6611 \r
6612 LRESULT CALLBACK\r
6613 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6614 {\r
6615   HANDLE hwndText;\r
6616   RECT rChild;\r
6617   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6618 \r
6619   switch (message) {\r
6620   case WM_INITDIALOG:\r
6621     GetWindowRect(hDlg, &rChild);\r
6622 \r
6623     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6624                                                              SWP_NOZORDER);\r
6625 \r
6626     /* \r
6627         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6628         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6629         and it doesn't work when you resize the dialog.\r
6630         For now, just give it a default position.\r
6631     */\r
6632     gothicDialog = hDlg;\r
6633     SetWindowText(hDlg, errorTitle);\r
6634     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6635     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6636     return FALSE;\r
6637 \r
6638   case WM_COMMAND:\r
6639     switch (LOWORD(wParam)) {\r
6640     case IDOK:\r
6641     case IDCANCEL:\r
6642       if (errorDialog == hDlg) errorDialog = NULL;\r
6643       DestroyWindow(hDlg);\r
6644       return TRUE;\r
6645 \r
6646     default:\r
6647       break;\r
6648     }\r
6649     break;\r
6650   }\r
6651   return FALSE;\r
6652 }\r
6653 \r
6654 VOID\r
6655 GothicPopUp(char *title, VariantClass variant)\r
6656 {\r
6657   FARPROC lpProc;\r
6658   static char *lastTitle;\r
6659 \r
6660   strncpy(errorTitle, title, sizeof(errorTitle));\r
6661   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6662 \r
6663   if(lastTitle != title && gothicDialog != NULL) {\r
6664     DestroyWindow(gothicDialog);\r
6665     gothicDialog = NULL;\r
6666   }\r
6667   if(variant != VariantNormal && gothicDialog == NULL) {\r
6668     title = lastTitle;\r
6669     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6670     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6671                  hwndMain, (DLGPROC)lpProc);\r
6672     FreeProcInstance(lpProc);\r
6673   }\r
6674 }\r
6675 #endif\r
6676 \r
6677 /*---------------------------------------------------------------------------*\\r
6678  *\r
6679  *  Ics Interaction console functions\r
6680  *\r
6681 \*---------------------------------------------------------------------------*/\r
6682 \r
6683 #define HISTORY_SIZE 64\r
6684 static char *history[HISTORY_SIZE];\r
6685 int histIn = 0, histP = 0;\r
6686 \r
6687 VOID\r
6688 SaveInHistory(char *cmd)\r
6689 {\r
6690   if (history[histIn] != NULL) {\r
6691     free(history[histIn]);\r
6692     history[histIn] = NULL;\r
6693   }\r
6694   if (*cmd == NULLCHAR) return;\r
6695   history[histIn] = StrSave(cmd);\r
6696   histIn = (histIn + 1) % HISTORY_SIZE;\r
6697   if (history[histIn] != NULL) {\r
6698     free(history[histIn]);\r
6699     history[histIn] = NULL;\r
6700   }\r
6701   histP = histIn;\r
6702 }\r
6703 \r
6704 char *\r
6705 PrevInHistory(char *cmd)\r
6706 {\r
6707   int newhp;\r
6708   if (histP == histIn) {\r
6709     if (history[histIn] != NULL) free(history[histIn]);\r
6710     history[histIn] = StrSave(cmd);\r
6711   }\r
6712   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6713   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6714   histP = newhp;\r
6715   return history[histP];\r
6716 }\r
6717 \r
6718 char *\r
6719 NextInHistory()\r
6720 {\r
6721   if (histP == histIn) return NULL;\r
6722   histP = (histP + 1) % HISTORY_SIZE;\r
6723   return history[histP];   \r
6724 }\r
6725 \r
6726 HMENU\r
6727 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6728 {\r
6729   HMENU hmenu, h;\r
6730   int i = 0;\r
6731   hmenu = LoadMenu(hInst, "TextMenu");\r
6732   h = GetSubMenu(hmenu, 0);\r
6733   while (e->item) {\r
6734     if (strcmp(e->item, "-") == 0) {\r
6735       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6736     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6737       int flags = MF_STRING, j = 0;\r
6738       if (e->item[0] == '|') {\r
6739         flags |= MF_MENUBARBREAK;\r
6740         j++;\r
6741       }\r
6742       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6743       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6744     }\r
6745     e++;\r
6746     i++;\r
6747   } \r
6748   return hmenu;\r
6749 }\r
6750 \r
6751 WNDPROC consoleTextWindowProc;\r
6752 \r
6753 void\r
6754 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6755 {\r
6756   char buf[MSG_SIZ], name[MSG_SIZ];\r
6757   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6758   CHARRANGE sel;\r
6759 \r
6760   if (!getname) {\r
6761     SetWindowText(hInput, command);\r
6762     if (immediate) {\r
6763       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6764     } else {\r
6765       sel.cpMin = 999999;\r
6766       sel.cpMax = 999999;\r
6767       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6768       SetFocus(hInput);\r
6769     }\r
6770     return;\r
6771   }    \r
6772   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6773   if (sel.cpMin == sel.cpMax) {\r
6774     /* Expand to surrounding word */\r
6775     TEXTRANGE tr;\r
6776     do {\r
6777       tr.chrg.cpMax = sel.cpMin;\r
6778       tr.chrg.cpMin = --sel.cpMin;\r
6779       if (sel.cpMin < 0) break;\r
6780       tr.lpstrText = name;\r
6781       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6782     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6783     sel.cpMin++;\r
6784 \r
6785     do {\r
6786       tr.chrg.cpMin = sel.cpMax;\r
6787       tr.chrg.cpMax = ++sel.cpMax;\r
6788       tr.lpstrText = name;\r
6789       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6790     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6791     sel.cpMax--;\r
6792 \r
6793     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6794       MessageBeep(MB_ICONEXCLAMATION);\r
6795       return;\r
6796     }\r
6797     tr.chrg = sel;\r
6798     tr.lpstrText = name;\r
6799     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6800   } else {\r
6801     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6802       MessageBeep(MB_ICONEXCLAMATION);\r
6803       return;\r
6804     }\r
6805     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6806   }\r
6807   if (immediate) {\r
6808     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6809     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6810     SetWindowText(hInput, buf);\r
6811     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6812   } else {\r
6813     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6814       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6815     SetWindowText(hInput, buf);\r
6816     sel.cpMin = 999999;\r
6817     sel.cpMax = 999999;\r
6818     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6819     SetFocus(hInput);\r
6820   }\r
6821 }\r
6822 \r
6823 LRESULT CALLBACK \r
6824 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6825 {\r
6826   HWND hInput;\r
6827   CHARRANGE sel;\r
6828 \r
6829   switch (message) {\r
6830   case WM_KEYDOWN:\r
6831     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6832     if(wParam=='R') return 0;\r
6833     switch (wParam) {\r
6834     case VK_PRIOR:\r
6835       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6836       return 0;\r
6837     case VK_NEXT:\r
6838       sel.cpMin = 999999;\r
6839       sel.cpMax = 999999;\r
6840       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6841       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6842       return 0;\r
6843     }\r
6844     break;\r
6845   case WM_CHAR:\r
6846    if(wParam != '\022') {\r
6847     if (wParam == '\t') {\r
6848       if (GetKeyState(VK_SHIFT) < 0) {\r
6849         /* shifted */\r
6850         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6851         if (buttonDesc[0].hwnd) {\r
6852           SetFocus(buttonDesc[0].hwnd);\r
6853         } else {\r
6854           SetFocus(hwndMain);\r
6855         }\r
6856       } else {\r
6857         /* unshifted */\r
6858         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6859       }\r
6860     } else {\r
6861       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6862       JAWS_DELETE( SetFocus(hInput); )\r
6863       SendMessage(hInput, message, wParam, lParam);\r
6864     }\r
6865     return 0;\r
6866    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6867    lParam = -1;\r
6868   case WM_RBUTTONDOWN:\r
6869     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6870       /* Move selection here if it was empty */\r
6871       POINT pt;\r
6872       pt.x = LOWORD(lParam);\r
6873       pt.y = HIWORD(lParam);\r
6874       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6875       if (sel.cpMin == sel.cpMax) {\r
6876         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6877         sel.cpMax = sel.cpMin;\r
6878         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6879       }\r
6880       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6881 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6882       POINT pt;\r
6883       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6884       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6885       if (sel.cpMin == sel.cpMax) {\r
6886         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6887         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6888       }\r
6889       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6890         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6891       }\r
6892       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6893       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6894       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6895       MenuPopup(hwnd, pt, hmenu, -1);\r
6896 }\r
6897     }\r
6898     return 0;\r
6899   case WM_RBUTTONUP:\r
6900     if (GetKeyState(VK_SHIFT) & ~1) {\r
6901       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6902         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6903     }\r
6904     return 0;\r
6905   case WM_PASTE:\r
6906     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6907     SetFocus(hInput);\r
6908     return SendMessage(hInput, message, wParam, lParam);\r
6909   case WM_MBUTTONDOWN:\r
6910     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6911   case WM_COMMAND:\r
6912     switch (LOWORD(wParam)) {\r
6913     case IDM_QuickPaste:\r
6914       {\r
6915         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6916         if (sel.cpMin == sel.cpMax) {\r
6917           MessageBeep(MB_ICONEXCLAMATION);\r
6918           return 0;\r
6919         }\r
6920         SendMessage(hwnd, WM_COPY, 0, 0);\r
6921         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6922         SendMessage(hInput, WM_PASTE, 0, 0);\r
6923         SetFocus(hInput);\r
6924         return 0;\r
6925       }\r
6926     case IDM_Cut:\r
6927       SendMessage(hwnd, WM_CUT, 0, 0);\r
6928       return 0;\r
6929     case IDM_Paste:\r
6930       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6931       return 0;\r
6932     case IDM_Copy:\r
6933       SendMessage(hwnd, WM_COPY, 0, 0);\r
6934       return 0;\r
6935     default:\r
6936       {\r
6937         int i = LOWORD(wParam) - IDM_CommandX;\r
6938         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6939             icsTextMenuEntry[i].command != NULL) {\r
6940           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6941                    icsTextMenuEntry[i].getname,\r
6942                    icsTextMenuEntry[i].immediate);\r
6943           return 0;\r
6944         }\r
6945       }\r
6946       break;\r
6947     }\r
6948     break;\r
6949   }\r
6950   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6951 }\r
6952 \r
6953 WNDPROC consoleInputWindowProc;\r
6954 \r
6955 LRESULT CALLBACK\r
6956 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6957 {\r
6958   char buf[MSG_SIZ];\r
6959   char *p;\r
6960   static BOOL sendNextChar = FALSE;\r
6961   static BOOL quoteNextChar = FALSE;\r
6962   InputSource *is = consoleInputSource;\r
6963   CHARFORMAT cf;\r
6964   CHARRANGE sel;\r
6965 \r
6966   switch (message) {\r
6967   case WM_CHAR:\r
6968     if (!appData.localLineEditing || sendNextChar) {\r
6969       is->buf[0] = (CHAR) wParam;\r
6970       is->count = 1;\r
6971       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6972       sendNextChar = FALSE;\r
6973       return 0;\r
6974     }\r
6975     if (quoteNextChar) {\r
6976       buf[0] = (char) wParam;\r
6977       buf[1] = NULLCHAR;\r
6978       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6979       quoteNextChar = FALSE;\r
6980       return 0;\r
6981     }\r
6982     switch (wParam) {\r
6983     case '\r':   /* Enter key */\r
6984       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6985       if (consoleEcho) SaveInHistory(is->buf);\r
6986       is->buf[is->count++] = '\n';\r
6987       is->buf[is->count] = NULLCHAR;\r
6988       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6989       if (consoleEcho) {\r
6990         ConsoleOutput(is->buf, is->count, TRUE);\r
6991       } else if (appData.localLineEditing) {\r
6992         ConsoleOutput("\n", 1, TRUE);\r
6993       }\r
6994       /* fall thru */\r
6995     case '\033': /* Escape key */\r
6996       SetWindowText(hwnd, "");\r
6997       cf.cbSize = sizeof(CHARFORMAT);\r
6998       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6999       if (consoleEcho) {\r
7000         cf.crTextColor = textAttribs[ColorNormal].color;\r
7001       } else {\r
7002         cf.crTextColor = COLOR_ECHOOFF;\r
7003       }\r
7004       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7005       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7006       return 0;\r
7007     case '\t':   /* Tab key */\r
7008       if (GetKeyState(VK_SHIFT) < 0) {\r
7009         /* shifted */\r
7010         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7011       } else {\r
7012         /* unshifted */\r
7013         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7014         if (buttonDesc[0].hwnd) {\r
7015           SetFocus(buttonDesc[0].hwnd);\r
7016         } else {\r
7017           SetFocus(hwndMain);\r
7018         }\r
7019       }\r
7020       return 0;\r
7021     case '\023': /* Ctrl+S */\r
7022       sendNextChar = TRUE;\r
7023       return 0;\r
7024     case '\021': /* Ctrl+Q */\r
7025       quoteNextChar = TRUE;\r
7026       return 0;\r
7027     JAWS_REPLAY\r
7028     default:\r
7029       break;\r
7030     }\r
7031     break;\r
7032   case WM_KEYDOWN:\r
7033     switch (wParam) {\r
7034     case VK_UP:\r
7035       GetWindowText(hwnd, buf, MSG_SIZ);\r
7036       p = PrevInHistory(buf);\r
7037       if (p != NULL) {\r
7038         SetWindowText(hwnd, p);\r
7039         sel.cpMin = 999999;\r
7040         sel.cpMax = 999999;\r
7041         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7042         return 0;\r
7043       }\r
7044       break;\r
7045     case VK_DOWN:\r
7046       p = NextInHistory();\r
7047       if (p != NULL) {\r
7048         SetWindowText(hwnd, p);\r
7049         sel.cpMin = 999999;\r
7050         sel.cpMax = 999999;\r
7051         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7052         return 0;\r
7053       }\r
7054       break;\r
7055     case VK_HOME:\r
7056     case VK_END:\r
7057       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7058       /* fall thru */\r
7059     case VK_PRIOR:\r
7060     case VK_NEXT:\r
7061       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7062       return 0;\r
7063     }\r
7064     break;\r
7065   case WM_MBUTTONDOWN:\r
7066     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7067       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7068     break;\r
7069   case WM_RBUTTONUP:\r
7070     if (GetKeyState(VK_SHIFT) & ~1) {\r
7071       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7072         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7073     } else {\r
7074       POINT pt;\r
7075       HMENU hmenu;\r
7076       hmenu = LoadMenu(hInst, "InputMenu");\r
7077       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7078       if (sel.cpMin == sel.cpMax) {\r
7079         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7080         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7081       }\r
7082       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7083         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7084       }\r
7085       pt.x = LOWORD(lParam);\r
7086       pt.y = HIWORD(lParam);\r
7087       MenuPopup(hwnd, pt, hmenu, -1);\r
7088     }\r
7089     return 0;\r
7090   case WM_COMMAND:\r
7091     switch (LOWORD(wParam)) { \r
7092     case IDM_Undo:\r
7093       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7094       return 0;\r
7095     case IDM_SelectAll:\r
7096       sel.cpMin = 0;\r
7097       sel.cpMax = -1; /*999999?*/\r
7098       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7099       return 0;\r
7100     case IDM_Cut:\r
7101       SendMessage(hwnd, WM_CUT, 0, 0);\r
7102       return 0;\r
7103     case IDM_Paste:\r
7104       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7105       return 0;\r
7106     case IDM_Copy:\r
7107       SendMessage(hwnd, WM_COPY, 0, 0);\r
7108       return 0;\r
7109     }\r
7110     break;\r
7111   }\r
7112   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7113 }\r
7114 \r
7115 #define CO_MAX  100000\r
7116 #define CO_TRIM   1000\r
7117 \r
7118 LRESULT CALLBACK\r
7119 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7120 {\r
7121   static SnapData sd;\r
7122   HWND hText, hInput;\r
7123   RECT rect;\r
7124   static int sizeX, sizeY;\r
7125   int newSizeX, newSizeY;\r
7126   MINMAXINFO *mmi;\r
7127   WORD wMask;\r
7128 \r
7129   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7130   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7131 \r
7132   switch (message) {\r
7133   case WM_NOTIFY:\r
7134     if (((NMHDR*)lParam)->code == EN_LINK)\r
7135     {\r
7136       ENLINK *pLink = (ENLINK*)lParam;\r
7137       if (pLink->msg == WM_LBUTTONUP)\r
7138       {\r
7139         TEXTRANGE tr;\r
7140 \r
7141         tr.chrg = pLink->chrg;\r
7142         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7143         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7144         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7145         free(tr.lpstrText);\r
7146       }\r
7147     }\r
7148     break;\r
7149   case WM_INITDIALOG: /* message: initialize dialog box */\r
7150     hwndConsole = hDlg;\r
7151     SetFocus(hInput);\r
7152     consoleTextWindowProc = (WNDPROC)\r
7153       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7154     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7155     consoleInputWindowProc = (WNDPROC)\r
7156       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7157     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7158     Colorize(ColorNormal, TRUE);\r
7159     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7160     ChangedConsoleFont();\r
7161     GetClientRect(hDlg, &rect);\r
7162     sizeX = rect.right;\r
7163     sizeY = rect.bottom;\r
7164     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7165         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7166       WINDOWPLACEMENT wp;\r
7167       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7168       wp.length = sizeof(WINDOWPLACEMENT);\r
7169       wp.flags = 0;\r
7170       wp.showCmd = SW_SHOW;\r
7171       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7172       wp.rcNormalPosition.left = wpConsole.x;\r
7173       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7174       wp.rcNormalPosition.top = wpConsole.y;\r
7175       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7176       SetWindowPlacement(hDlg, &wp);\r
7177     }\r
7178 \r
7179    // [HGM] Chessknight's change 2004-07-13\r
7180    else { /* Determine Defaults */\r
7181        WINDOWPLACEMENT wp;\r
7182        wpConsole.x = wpMain.width + 1;\r
7183        wpConsole.y = wpMain.y;\r
7184        wpConsole.width = screenWidth -  wpMain.width;\r
7185        wpConsole.height = wpMain.height;\r
7186        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7187        wp.length = sizeof(WINDOWPLACEMENT);\r
7188        wp.flags = 0;\r
7189        wp.showCmd = SW_SHOW;\r
7190        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7191        wp.rcNormalPosition.left = wpConsole.x;\r
7192        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7193        wp.rcNormalPosition.top = wpConsole.y;\r
7194        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7195        SetWindowPlacement(hDlg, &wp);\r
7196     }\r
7197 \r
7198    // Allow hText to highlight URLs and send notifications on them\r
7199    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7200    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7201    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7202    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7203 \r
7204     return FALSE;\r
7205 \r
7206   case WM_SETFOCUS:\r
7207     SetFocus(hInput);\r
7208     return 0;\r
7209 \r
7210   case WM_CLOSE:\r
7211     ExitEvent(0);\r
7212     /* not reached */\r
7213     break;\r
7214 \r
7215   case WM_SIZE:\r
7216     if (IsIconic(hDlg)) break;\r
7217     newSizeX = LOWORD(lParam);\r
7218     newSizeY = HIWORD(lParam);\r
7219     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7220       RECT rectText, rectInput;\r
7221       POINT pt;\r
7222       int newTextHeight, newTextWidth;\r
7223       GetWindowRect(hText, &rectText);\r
7224       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7225       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7226       if (newTextHeight < 0) {\r
7227         newSizeY += -newTextHeight;\r
7228         newTextHeight = 0;\r
7229       }\r
7230       SetWindowPos(hText, NULL, 0, 0,\r
7231         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7232       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7233       pt.x = rectInput.left;\r
7234       pt.y = rectInput.top + newSizeY - sizeY;\r
7235       ScreenToClient(hDlg, &pt);\r
7236       SetWindowPos(hInput, NULL, \r
7237         pt.x, pt.y, /* needs client coords */   \r
7238         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7239         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7240     }\r
7241     sizeX = newSizeX;\r
7242     sizeY = newSizeY;\r
7243     break;\r
7244 \r
7245   case WM_GETMINMAXINFO:\r
7246     /* Prevent resizing window too small */\r
7247     mmi = (MINMAXINFO *) lParam;\r
7248     mmi->ptMinTrackSize.x = 100;\r
7249     mmi->ptMinTrackSize.y = 100;\r
7250     break;\r
7251 \r
7252   /* [AS] Snapping */\r
7253   case WM_ENTERSIZEMOVE:\r
7254     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7255 \r
7256   case WM_SIZING:\r
7257     return OnSizing( &sd, hDlg, wParam, lParam );\r
7258 \r
7259   case WM_MOVING:\r
7260     return OnMoving( &sd, hDlg, wParam, lParam );\r
7261 \r
7262   case WM_EXITSIZEMOVE:\r
7263         UpdateICSWidth(hText);\r
7264     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7265   }\r
7266 \r
7267   return DefWindowProc(hDlg, message, wParam, lParam);\r
7268 }\r
7269 \r
7270 \r
7271 VOID\r
7272 ConsoleCreate()\r
7273 {\r
7274   HWND hCons;\r
7275   if (hwndConsole) return;\r
7276   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7277   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7278 }\r
7279 \r
7280 \r
7281 VOID\r
7282 ConsoleOutput(char* data, int length, int forceVisible)\r
7283 {\r
7284   HWND hText;\r
7285   int trim, exlen;\r
7286   char *p, *q;\r
7287   char buf[CO_MAX+1];\r
7288   POINT pEnd;\r
7289   RECT rect;\r
7290   static int delayLF = 0;\r
7291   CHARRANGE savesel, sel;\r
7292 \r
7293   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7294   p = data;\r
7295   q = buf;\r
7296   if (delayLF) {\r
7297     *q++ = '\r';\r
7298     *q++ = '\n';\r
7299     delayLF = 0;\r
7300   }\r
7301   while (length--) {\r
7302     if (*p == '\n') {\r
7303       if (*++p) {\r
7304         *q++ = '\r';\r
7305         *q++ = '\n';\r
7306       } else {\r
7307         delayLF = 1;\r
7308       }\r
7309     } else if (*p == '\007') {\r
7310        MyPlaySound(&sounds[(int)SoundBell]);\r
7311        p++;\r
7312     } else {\r
7313       *q++ = *p++;\r
7314     }\r
7315   }\r
7316   *q = NULLCHAR;\r
7317   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7318   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7319   /* Save current selection */\r
7320   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7321   exlen = GetWindowTextLength(hText);\r
7322   /* Find out whether current end of text is visible */\r
7323   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7324   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7325   /* Trim existing text if it's too long */\r
7326   if (exlen + (q - buf) > CO_MAX) {\r
7327     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7328     sel.cpMin = 0;\r
7329     sel.cpMax = trim;\r
7330     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7331     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7332     exlen -= trim;\r
7333     savesel.cpMin -= trim;\r
7334     savesel.cpMax -= trim;\r
7335     if (exlen < 0) exlen = 0;\r
7336     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7337     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7338   }\r
7339   /* Append the new text */\r
7340   sel.cpMin = exlen;\r
7341   sel.cpMax = exlen;\r
7342   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7343   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7344   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7345   if (forceVisible || exlen == 0 ||\r
7346       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7347        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7348     /* Scroll to make new end of text visible if old end of text\r
7349        was visible or new text is an echo of user typein */\r
7350     sel.cpMin = 9999999;\r
7351     sel.cpMax = 9999999;\r
7352     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7353     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7354     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7355     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7356   }\r
7357   if (savesel.cpMax == exlen || forceVisible) {\r
7358     /* Move insert point to new end of text if it was at the old\r
7359        end of text or if the new text is an echo of user typein */\r
7360     sel.cpMin = 9999999;\r
7361     sel.cpMax = 9999999;\r
7362     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7363   } else {\r
7364     /* Restore previous selection */\r
7365     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7366   }\r
7367   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7368 }\r
7369 \r
7370 /*---------*/\r
7371 \r
7372 \r
7373 void\r
7374 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7375 {\r
7376   char buf[100];\r
7377   char *str;\r
7378   COLORREF oldFg, oldBg;\r
7379   HFONT oldFont;\r
7380   RECT rect;\r
7381 \r
7382   if(copyNumber > 1)
7383     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7384 \r
7385   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7386   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7387   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7388 \r
7389   rect.left = x;\r
7390   rect.right = x + squareSize;\r
7391   rect.top  = y;\r
7392   rect.bottom = y + squareSize;\r
7393   str = buf;\r
7394 \r
7395   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7396                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7397              y, ETO_CLIPPED|ETO_OPAQUE,\r
7398              &rect, str, strlen(str), NULL);\r
7399 \r
7400   (void) SetTextColor(hdc, oldFg);\r
7401   (void) SetBkColor(hdc, oldBg);\r
7402   (void) SelectObject(hdc, oldFont);\r
7403 }\r
7404 \r
7405 void\r
7406 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7407               RECT *rect, char *color, char *flagFell)\r
7408 {\r
7409   char buf[100];\r
7410   char *str;\r
7411   COLORREF oldFg, oldBg;\r
7412   HFONT oldFont;\r
7413 \r
7414   if (appData.clockMode) {\r
7415     if (tinyLayout)\r
7416       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7417     else\r
7418       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7419     str = buf;\r
7420   } else {\r
7421     str = color;\r
7422   }\r
7423 \r
7424   if (highlight) {\r
7425     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7426     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7427   } else {\r
7428     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7429     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7430   }\r
7431   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7432 \r
7433   JAWS_SILENCE\r
7434 \r
7435   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7436              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7437              rect, str, strlen(str), NULL);\r
7438   if(logoHeight > 0 && appData.clockMode) {\r
7439       RECT r;\r
7440       str += strlen(color)+2;\r
7441       r.top = rect->top + logoHeight/2;\r
7442       r.left = rect->left;\r
7443       r.right = rect->right;\r
7444       r.bottom = rect->bottom;\r
7445       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7446                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7447                  &r, str, strlen(str), NULL);\r
7448   }\r
7449   (void) SetTextColor(hdc, oldFg);\r
7450   (void) SetBkColor(hdc, oldBg);\r
7451   (void) SelectObject(hdc, oldFont);\r
7452 }\r
7453 \r
7454 \r
7455 int\r
7456 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7457            OVERLAPPED *ovl)\r
7458 {\r
7459   int ok, err;\r
7460 \r
7461   /* [AS]  */\r
7462   if( count <= 0 ) {\r
7463     if (appData.debugMode) {\r
7464       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7465     }\r
7466 \r
7467     return ERROR_INVALID_USER_BUFFER;\r
7468   }\r
7469 \r
7470   ResetEvent(ovl->hEvent);\r
7471   ovl->Offset = ovl->OffsetHigh = 0;\r
7472   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7473   if (ok) {\r
7474     err = NO_ERROR;\r
7475   } else {\r
7476     err = GetLastError();\r
7477     if (err == ERROR_IO_PENDING) {\r
7478       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7479       if (ok)\r
7480         err = NO_ERROR;\r
7481       else\r
7482         err = GetLastError();\r
7483     }\r
7484   }\r
7485   return err;\r
7486 }\r
7487 \r
7488 int\r
7489 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7490             OVERLAPPED *ovl)\r
7491 {\r
7492   int ok, err;\r
7493 \r
7494   ResetEvent(ovl->hEvent);\r
7495   ovl->Offset = ovl->OffsetHigh = 0;\r
7496   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7497   if (ok) {\r
7498     err = NO_ERROR;\r
7499   } else {\r
7500     err = GetLastError();\r
7501     if (err == ERROR_IO_PENDING) {\r
7502       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7503       if (ok)\r
7504         err = NO_ERROR;\r
7505       else\r
7506         err = GetLastError();\r
7507     }\r
7508   }\r
7509   return err;\r
7510 }\r
7511 \r
7512 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7513 void CheckForInputBufferFull( InputSource * is )\r
7514 {\r
7515     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7516         /* Look for end of line */\r
7517         char * p = is->buf;\r
7518         \r
7519         while( p < is->next && *p != '\n' ) {\r
7520             p++;\r
7521         }\r
7522 \r
7523         if( p >= is->next ) {\r
7524             if (appData.debugMode) {\r
7525                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7526             }\r
7527 \r
7528             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7529             is->count = (DWORD) -1;\r
7530             is->next = is->buf;\r
7531         }\r
7532     }\r
7533 }\r
7534 \r
7535 DWORD\r
7536 InputThread(LPVOID arg)\r
7537 {\r
7538   InputSource *is;\r
7539   OVERLAPPED ovl;\r
7540 \r
7541   is = (InputSource *) arg;\r
7542   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7543   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7544   while (is->hThread != NULL) {\r
7545     is->error = DoReadFile(is->hFile, is->next,\r
7546                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7547                            &is->count, &ovl);\r
7548     if (is->error == NO_ERROR) {\r
7549       is->next += is->count;\r
7550     } else {\r
7551       if (is->error == ERROR_BROKEN_PIPE) {\r
7552         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7553         is->count = 0;\r
7554       } else {\r
7555         is->count = (DWORD) -1;\r
7556         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7557         break; \r
7558       }\r
7559     }\r
7560 \r
7561     CheckForInputBufferFull( is );\r
7562 \r
7563     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7564 \r
7565     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7566 \r
7567     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7568   }\r
7569 \r
7570   CloseHandle(ovl.hEvent);\r
7571   CloseHandle(is->hFile);\r
7572 \r
7573   if (appData.debugMode) {\r
7574     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7575   }\r
7576 \r
7577   return 0;\r
7578 }\r
7579 \r
7580 \r
7581 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7582 DWORD\r
7583 NonOvlInputThread(LPVOID arg)\r
7584 {\r
7585   InputSource *is;\r
7586   char *p, *q;\r
7587   int i;\r
7588   char prev;\r
7589 \r
7590   is = (InputSource *) arg;\r
7591   while (is->hThread != NULL) {\r
7592     is->error = ReadFile(is->hFile, is->next,\r
7593                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7594                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7595     if (is->error == NO_ERROR) {\r
7596       /* Change CRLF to LF */\r
7597       if (is->next > is->buf) {\r
7598         p = is->next - 1;\r
7599         i = is->count + 1;\r
7600       } else {\r
7601         p = is->next;\r
7602         i = is->count;\r
7603       }\r
7604       q = p;\r
7605       prev = NULLCHAR;\r
7606       while (i > 0) {\r
7607         if (prev == '\r' && *p == '\n') {\r
7608           *(q-1) = '\n';\r
7609           is->count--;\r
7610         } else { \r
7611           *q++ = *p;\r
7612         }\r
7613         prev = *p++;\r
7614         i--;\r
7615       }\r
7616       *q = NULLCHAR;\r
7617       is->next = q;\r
7618     } else {\r
7619       if (is->error == ERROR_BROKEN_PIPE) {\r
7620         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7621         is->count = 0; \r
7622       } else {\r
7623         is->count = (DWORD) -1;\r
7624       }\r
7625     }\r
7626 \r
7627     CheckForInputBufferFull( is );\r
7628 \r
7629     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7630 \r
7631     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7632 \r
7633     if (is->count < 0) break;  /* Quit on error */\r
7634   }\r
7635   CloseHandle(is->hFile);\r
7636   return 0;\r
7637 }\r
7638 \r
7639 DWORD\r
7640 SocketInputThread(LPVOID arg)\r
7641 {\r
7642   InputSource *is;\r
7643 \r
7644   is = (InputSource *) arg;\r
7645   while (is->hThread != NULL) {\r
7646     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7647     if ((int)is->count == SOCKET_ERROR) {\r
7648       is->count = (DWORD) -1;\r
7649       is->error = WSAGetLastError();\r
7650     } else {\r
7651       is->error = NO_ERROR;\r
7652       is->next += is->count;\r
7653       if (is->count == 0 && is->second == is) {\r
7654         /* End of file on stderr; quit with no message */\r
7655         break;\r
7656       }\r
7657     }\r
7658     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7659 \r
7660     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7661 \r
7662     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7663   }\r
7664   return 0;\r
7665 }\r
7666 \r
7667 VOID\r
7668 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7669 {\r
7670   InputSource *is;\r
7671 \r
7672   is = (InputSource *) lParam;\r
7673   if (is->lineByLine) {\r
7674     /* Feed in lines one by one */\r
7675     char *p = is->buf;\r
7676     char *q = p;\r
7677     while (q < is->next) {\r
7678       if (*q++ == '\n') {\r
7679         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7680         p = q;\r
7681       }\r
7682     }\r
7683     \r
7684     /* Move any partial line to the start of the buffer */\r
7685     q = is->buf;\r
7686     while (p < is->next) {\r
7687       *q++ = *p++;\r
7688     }\r
7689     is->next = q;\r
7690 \r
7691     if (is->error != NO_ERROR || is->count == 0) {\r
7692       /* Notify backend of the error.  Note: If there was a partial\r
7693          line at the end, it is not flushed through. */\r
7694       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7695     }\r
7696   } else {\r
7697     /* Feed in the whole chunk of input at once */\r
7698     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7699     is->next = is->buf;\r
7700   }\r
7701 }\r
7702 \r
7703 /*---------------------------------------------------------------------------*\\r
7704  *\r
7705  *  Menu enables. Used when setting various modes.\r
7706  *\r
7707 \*---------------------------------------------------------------------------*/\r
7708 \r
7709 typedef struct {\r
7710   int item;\r
7711   int flags;\r
7712 } Enables;\r
7713 \r
7714 VOID\r
7715 GreyRevert(Boolean grey)\r
7716 { // [HGM] vari: for retracting variations in local mode\r
7717   HMENU hmenu = GetMenu(hwndMain);\r
7718   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7719   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7720 }\r
7721 \r
7722 VOID\r
7723 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7724 {\r
7725   while (enab->item > 0) {\r
7726     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7727     enab++;\r
7728   }\r
7729 }\r
7730 \r
7731 Enables gnuEnables[] = {\r
7732   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7733   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7734   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7735   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7736   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7737   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7738   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7739   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7740   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7745   { -1, -1 }\r
7746 };\r
7747 \r
7748 Enables icsEnables[] = {\r
7749   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7751   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7753   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7754   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7755   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7756   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7757   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7763   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7764   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7765   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7766   { -1, -1 }\r
7767 };\r
7768 \r
7769 #if ZIPPY\r
7770 Enables zippyEnables[] = {\r
7771   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7772   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7773   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7774   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7775   { -1, -1 }\r
7776 };\r
7777 #endif\r
7778 \r
7779 Enables ncpEnables[] = {\r
7780   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7789   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7790   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7802   { -1, -1 }\r
7803 };\r
7804 \r
7805 Enables trainingOnEnables[] = {\r
7806   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7808   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7809   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7810   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7811   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7812   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7813   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7815   { -1, -1 }\r
7816 };\r
7817 \r
7818 Enables trainingOffEnables[] = {\r
7819   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7820   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7821   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7822   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7823   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7824   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7825   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7826   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7827   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7828   { -1, -1 }\r
7829 };\r
7830 \r
7831 /* These modify either ncpEnables or gnuEnables */\r
7832 Enables cmailEnables[] = {\r
7833   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7834   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7835   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7836   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7838   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7840   { -1, -1 }\r
7841 };\r
7842 \r
7843 Enables machineThinkingEnables[] = {\r
7844   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7857 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7860   { -1, -1 }\r
7861 };\r
7862 \r
7863 Enables userThinkingEnables[] = {\r
7864   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7868   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7870   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7871   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7872   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7877 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7880   { -1, -1 }\r
7881 };\r
7882 \r
7883 /*---------------------------------------------------------------------------*\\r
7884  *\r
7885  *  Front-end interface functions exported by XBoard.\r
7886  *  Functions appear in same order as prototypes in frontend.h.\r
7887  * \r
7888 \*---------------------------------------------------------------------------*/\r
7889 VOID\r
7890 CheckMark(UINT item, int state)\r
7891 {\r
7892     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7893 }\r
7894 \r
7895 VOID\r
7896 ModeHighlight()\r
7897 {\r
7898   static UINT prevChecked = 0;\r
7899   static int prevPausing = 0;\r
7900   UINT nowChecked;\r
7901 \r
7902   if (pausing != prevPausing) {\r
7903     prevPausing = pausing;\r
7904     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7905                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7906     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7907   }\r
7908 \r
7909   switch (gameMode) {\r
7910   case BeginningOfGame:\r
7911     if (appData.icsActive)\r
7912       nowChecked = IDM_IcsClient;\r
7913     else if (appData.noChessProgram)\r
7914       nowChecked = IDM_EditGame;\r
7915     else\r
7916       nowChecked = IDM_MachineBlack;\r
7917     break;\r
7918   case MachinePlaysBlack:\r
7919     nowChecked = IDM_MachineBlack;\r
7920     break;\r
7921   case MachinePlaysWhite:\r
7922     nowChecked = IDM_MachineWhite;\r
7923     break;\r
7924   case TwoMachinesPlay:\r
7925     nowChecked = IDM_TwoMachines;\r
7926     break;\r
7927   case AnalyzeMode:\r
7928     nowChecked = IDM_AnalysisMode;\r
7929     break;\r
7930   case AnalyzeFile:\r
7931     nowChecked = IDM_AnalyzeFile;\r
7932     break;\r
7933   case EditGame:\r
7934     nowChecked = IDM_EditGame;\r
7935     break;\r
7936   case PlayFromGameFile:\r
7937     nowChecked = IDM_LoadGame;\r
7938     break;\r
7939   case EditPosition:\r
7940     nowChecked = IDM_EditPosition;\r
7941     break;\r
7942   case Training:\r
7943     nowChecked = IDM_Training;\r
7944     break;\r
7945   case IcsPlayingWhite:\r
7946   case IcsPlayingBlack:\r
7947   case IcsObserving:\r
7948   case IcsIdle:\r
7949     nowChecked = IDM_IcsClient;\r
7950     break;\r
7951   default:\r
7952   case EndOfGame:\r
7953     nowChecked = 0;\r
7954     break;\r
7955   }\r
7956   CheckMark(prevChecked, MF_UNCHECKED);\r
7957   CheckMark(nowChecked, MF_CHECKED);\r
7958   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
7959 \r
7960   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7961     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7962                           MF_BYCOMMAND|MF_ENABLED);\r
7963   } else {\r
7964     (void) EnableMenuItem(GetMenu(hwndMain), \r
7965                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7966   }\r
7967 \r
7968   prevChecked = nowChecked;\r
7969 \r
7970   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7971   if (appData.icsActive) {\r
7972        if (appData.icsEngineAnalyze) {\r
7973                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
7974        } else {\r
7975                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
7976        }\r
7977   }\r
7978   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
7979 }\r
7980 \r
7981 VOID\r
7982 SetICSMode()\r
7983 {\r
7984   HMENU hmenu = GetMenu(hwndMain);\r
7985   SetMenuEnables(hmenu, icsEnables);\r
7986   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
7987     MF_BYCOMMAND|MF_ENABLED);\r
7988 #if ZIPPY\r
7989   if (appData.zippyPlay) {\r
7990     SetMenuEnables(hmenu, zippyEnables);\r
7991     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7992          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7993           MF_BYCOMMAND|MF_ENABLED);\r
7994   }\r
7995 #endif\r
7996 }\r
7997 \r
7998 VOID\r
7999 SetGNUMode()\r
8000 {\r
8001   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8002 }\r
8003 \r
8004 VOID\r
8005 SetNCPMode()\r
8006 {\r
8007   HMENU hmenu = GetMenu(hwndMain);\r
8008   SetMenuEnables(hmenu, ncpEnables);\r
8009     DrawMenuBar(hwndMain);\r
8010 }\r
8011 \r
8012 VOID\r
8013 SetCmailMode()\r
8014 {\r
8015   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8016 }\r
8017 \r
8018 VOID \r
8019 SetTrainingModeOn()\r
8020 {\r
8021   int i;\r
8022   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8023   for (i = 0; i < N_BUTTONS; i++) {\r
8024     if (buttonDesc[i].hwnd != NULL)\r
8025       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8026   }\r
8027   CommentPopDown();\r
8028 }\r
8029 \r
8030 VOID SetTrainingModeOff()\r
8031 {\r
8032   int i;\r
8033   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8034   for (i = 0; i < N_BUTTONS; i++) {\r
8035     if (buttonDesc[i].hwnd != NULL)\r
8036       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8037   }\r
8038 }\r
8039 \r
8040 \r
8041 VOID\r
8042 SetUserThinkingEnables()\r
8043 {\r
8044   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8045 }\r
8046 \r
8047 VOID\r
8048 SetMachineThinkingEnables()\r
8049 {\r
8050   HMENU hMenu = GetMenu(hwndMain);\r
8051   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8052 \r
8053   SetMenuEnables(hMenu, machineThinkingEnables);\r
8054 \r
8055   if (gameMode == MachinePlaysBlack) {\r
8056     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8057   } else if (gameMode == MachinePlaysWhite) {\r
8058     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8059   } else if (gameMode == TwoMachinesPlay) {\r
8060     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8061   }\r
8062 }\r
8063 \r
8064 \r
8065 VOID\r
8066 DisplayTitle(char *str)\r
8067 {\r
8068   char title[MSG_SIZ], *host;\r
8069   if (str[0] != NULLCHAR) {\r
8070     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8071   } else if (appData.icsActive) {\r
8072     if (appData.icsCommPort[0] != NULLCHAR)\r
8073       host = "ICS";\r
8074     else \r
8075       host = appData.icsHost;\r
8076       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8077   } else if (appData.noChessProgram) {\r
8078     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8079   } else {\r
8080     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8081     strcat(title, ": ");\r
8082     strcat(title, first.tidy);\r
8083   }\r
8084   SetWindowText(hwndMain, title);\r
8085 }\r
8086 \r
8087 \r
8088 VOID\r
8089 DisplayMessage(char *str1, char *str2)\r
8090 {\r
8091   HDC hdc;\r
8092   HFONT oldFont;\r
8093   int remain = MESSAGE_TEXT_MAX - 1;\r
8094   int len;\r
8095 \r
8096   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8097   messageText[0] = NULLCHAR;\r
8098   if (*str1) {\r
8099     len = strlen(str1);\r
8100     if (len > remain) len = remain;\r
8101     strncpy(messageText, str1, len);\r
8102     messageText[len] = NULLCHAR;\r
8103     remain -= len;\r
8104   }\r
8105   if (*str2 && remain >= 2) {\r
8106     if (*str1) {\r
8107       strcat(messageText, "  ");\r
8108       remain -= 2;\r
8109     }\r
8110     len = strlen(str2);\r
8111     if (len > remain) len = remain;\r
8112     strncat(messageText, str2, len);\r
8113   }\r
8114   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8115   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8116 \r
8117   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8118 \r
8119   SAYMACHINEMOVE();\r
8120 \r
8121   hdc = GetDC(hwndMain);\r
8122   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8123   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8124              &messageRect, messageText, strlen(messageText), NULL);\r
8125   (void) SelectObject(hdc, oldFont);\r
8126   (void) ReleaseDC(hwndMain, hdc);\r
8127 }\r
8128 \r
8129 VOID\r
8130 DisplayError(char *str, int error)\r
8131 {\r
8132   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8133   int len;\r
8134 \r
8135   if (error == 0) {\r
8136     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8137   } else {\r
8138     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8139                         NULL, error, LANG_NEUTRAL,\r
8140                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8141     if (len > 0) {\r
8142       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8143     } else {\r
8144       ErrorMap *em = errmap;\r
8145       while (em->err != 0 && em->err != error) em++;\r
8146       if (em->err != 0) {\r
8147         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8148       } else {\r
8149         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8150       }\r
8151     }\r
8152   }\r
8153   \r
8154   ErrorPopUp(_("Error"), buf);\r
8155 }\r
8156 \r
8157 \r
8158 VOID\r
8159 DisplayMoveError(char *str)\r
8160 {\r
8161   fromX = fromY = -1;\r
8162   ClearHighlights();\r
8163   DrawPosition(FALSE, NULL);\r
8164   if (appData.popupMoveErrors) {\r
8165     ErrorPopUp(_("Error"), str);\r
8166   } else {\r
8167     DisplayMessage(str, "");\r
8168     moveErrorMessageUp = TRUE;\r
8169   }\r
8170 }\r
8171 \r
8172 VOID\r
8173 DisplayFatalError(char *str, int error, int exitStatus)\r
8174 {\r
8175   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8176   int len;\r
8177   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8178 \r
8179   if (error != 0) {\r
8180     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8181                         NULL, error, LANG_NEUTRAL,\r
8182                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8183     if (len > 0) {\r
8184       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8185     } else {\r
8186       ErrorMap *em = errmap;\r
8187       while (em->err != 0 && em->err != error) em++;\r
8188       if (em->err != 0) {\r
8189         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8190       } else {\r
8191         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8192       }\r
8193     }\r
8194     str = buf;\r
8195   }\r
8196   if (appData.debugMode) {\r
8197     fprintf(debugFP, "%s: %s\n", label, str);\r
8198   }\r
8199   if (appData.popupExitMessage) {\r
8200     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8201                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8202   }\r
8203   ExitEvent(exitStatus);\r
8204 }\r
8205 \r
8206 \r
8207 VOID\r
8208 DisplayInformation(char *str)\r
8209 {\r
8210   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8211 }\r
8212 \r
8213 \r
8214 VOID\r
8215 DisplayNote(char *str)\r
8216 {\r
8217   ErrorPopUp(_("Note"), str);\r
8218 }\r
8219 \r
8220 \r
8221 typedef struct {\r
8222   char *title, *question, *replyPrefix;\r
8223   ProcRef pr;\r
8224 } QuestionParams;\r
8225 \r
8226 LRESULT CALLBACK\r
8227 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8228 {\r
8229   static QuestionParams *qp;\r
8230   char reply[MSG_SIZ];\r
8231   int len, err;\r
8232 \r
8233   switch (message) {\r
8234   case WM_INITDIALOG:\r
8235     qp = (QuestionParams *) lParam;\r
8236     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8237     Translate(hDlg, DLG_Question);\r
8238     SetWindowText(hDlg, qp->title);\r
8239     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8240     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8241     return FALSE;\r
8242 \r
8243   case WM_COMMAND:\r
8244     switch (LOWORD(wParam)) {\r
8245     case IDOK:\r
8246       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8247       if (*reply) strcat(reply, " ");\r
8248       len = strlen(reply);\r
8249       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8250       strcat(reply, "\n");\r
8251       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8252       EndDialog(hDlg, TRUE);\r
8253       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8254       return TRUE;\r
8255     case IDCANCEL:\r
8256       EndDialog(hDlg, FALSE);\r
8257       return TRUE;\r
8258     default:\r
8259       break;\r
8260     }\r
8261     break;\r
8262   }\r
8263   return FALSE;\r
8264 }\r
8265 \r
8266 VOID\r
8267 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8268 {\r
8269     QuestionParams qp;\r
8270     FARPROC lpProc;\r
8271     \r
8272     qp.title = title;\r
8273     qp.question = question;\r
8274     qp.replyPrefix = replyPrefix;\r
8275     qp.pr = pr;\r
8276     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8277     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8278       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8279     FreeProcInstance(lpProc);\r
8280 }\r
8281 \r
8282 /* [AS] Pick FRC position */\r
8283 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8284 {\r
8285     static int * lpIndexFRC;\r
8286     BOOL index_is_ok;\r
8287     char buf[16];\r
8288 \r
8289     switch( message )\r
8290     {\r
8291     case WM_INITDIALOG:\r
8292         lpIndexFRC = (int *) lParam;\r
8293 \r
8294         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8295         Translate(hDlg, DLG_NewGameFRC);\r
8296 \r
8297         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8298         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8299         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8300         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8301 \r
8302         break;\r
8303 \r
8304     case WM_COMMAND:\r
8305         switch( LOWORD(wParam) ) {\r
8306         case IDOK:\r
8307             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8308             EndDialog( hDlg, 0 );\r
8309             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8310             return TRUE;\r
8311         case IDCANCEL:\r
8312             EndDialog( hDlg, 1 );   \r
8313             return TRUE;\r
8314         case IDC_NFG_Edit:\r
8315             if( HIWORD(wParam) == EN_CHANGE ) {\r
8316                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8317 \r
8318                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8319             }\r
8320             return TRUE;\r
8321         case IDC_NFG_Random:\r
8322           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8323             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8324             return TRUE;\r
8325         }\r
8326 \r
8327         break;\r
8328     }\r
8329 \r
8330     return FALSE;\r
8331 }\r
8332 \r
8333 int NewGameFRC()\r
8334 {\r
8335     int result;\r
8336     int index = appData.defaultFrcPosition;\r
8337     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8338 \r
8339     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8340 \r
8341     if( result == 0 ) {\r
8342         appData.defaultFrcPosition = index;\r
8343     }\r
8344 \r
8345     return result;\r
8346 }\r
8347 \r
8348 /* [AS] Game list options. Refactored by HGM */\r
8349 \r
8350 HWND gameListOptionsDialog;\r
8351 \r
8352 // low-level front-end: clear text edit / list widget\r
8353 void\r
8354 GLT_ClearList()\r
8355 {\r
8356     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8357 }\r
8358 \r
8359 // low-level front-end: clear text edit / list widget\r
8360 void\r
8361 GLT_DeSelectList()\r
8362 {\r
8363     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8364 }\r
8365 \r
8366 // low-level front-end: append line to text edit / list widget\r
8367 void\r
8368 GLT_AddToList( char *name )\r
8369 {\r
8370     if( name != 0 ) {\r
8371             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8372     }\r
8373 }\r
8374 \r
8375 // low-level front-end: get line from text edit / list widget\r
8376 Boolean\r
8377 GLT_GetFromList( int index, char *name )\r
8378 {\r
8379     if( name != 0 ) {\r
8380             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8381                 return TRUE;\r
8382     }\r
8383     return FALSE;\r
8384 }\r
8385 \r
8386 void GLT_MoveSelection( HWND hDlg, int delta )\r
8387 {\r
8388     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8389     int idx2 = idx1 + delta;\r
8390     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8391 \r
8392     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8393         char buf[128];\r
8394 \r
8395         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8396         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8397         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8398         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8399     }\r
8400 }\r
8401 \r
8402 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8403 {\r
8404     switch( message )\r
8405     {\r
8406     case WM_INITDIALOG:\r
8407         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8408         \r
8409         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8410         Translate(hDlg, DLG_GameListOptions);\r
8411 \r
8412         /* Initialize list */\r
8413         GLT_TagsToList( lpUserGLT );\r
8414 \r
8415         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8416 \r
8417         break;\r
8418 \r
8419     case WM_COMMAND:\r
8420         switch( LOWORD(wParam) ) {\r
8421         case IDOK:\r
8422             GLT_ParseList();\r
8423             EndDialog( hDlg, 0 );\r
8424             return TRUE;\r
8425         case IDCANCEL:\r
8426             EndDialog( hDlg, 1 );\r
8427             return TRUE;\r
8428 \r
8429         case IDC_GLT_Default:\r
8430             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8431             return TRUE;\r
8432 \r
8433         case IDC_GLT_Restore:\r
8434             GLT_TagsToList( appData.gameListTags );\r
8435             return TRUE;\r
8436 \r
8437         case IDC_GLT_Up:\r
8438             GLT_MoveSelection( hDlg, -1 );\r
8439             return TRUE;\r
8440 \r
8441         case IDC_GLT_Down:\r
8442             GLT_MoveSelection( hDlg, +1 );\r
8443             return TRUE;\r
8444         }\r
8445 \r
8446         break;\r
8447     }\r
8448 \r
8449     return FALSE;\r
8450 }\r
8451 \r
8452 int GameListOptions()\r
8453 {\r
8454     int result;\r
8455     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8456 \r
8457       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8458 \r
8459     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8460 \r
8461     if( result == 0 ) {\r
8462         /* [AS] Memory leak here! */\r
8463         appData.gameListTags = strdup( lpUserGLT ); \r
8464     }\r
8465 \r
8466     return result;\r
8467 }\r
8468 \r
8469 VOID\r
8470 DisplayIcsInteractionTitle(char *str)\r
8471 {\r
8472   char consoleTitle[MSG_SIZ];\r
8473 \r
8474     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8475   SetWindowText(hwndConsole, consoleTitle);\r
8476 }\r
8477 \r
8478 void\r
8479 DrawPosition(int fullRedraw, Board board)\r
8480 {\r
8481   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8482 }\r
8483 \r
8484 void NotifyFrontendLogin()\r
8485 {\r
8486         if (hwndConsole)\r
8487                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8488 }\r
8489 \r
8490 VOID\r
8491 ResetFrontEnd()\r
8492 {\r
8493   fromX = fromY = -1;\r
8494   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8495     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8496     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8497     dragInfo.lastpos = dragInfo.pos;\r
8498     dragInfo.start.x = dragInfo.start.y = -1;\r
8499     dragInfo.from = dragInfo.start;\r
8500     ReleaseCapture();\r
8501     DrawPosition(TRUE, NULL);\r
8502   }\r
8503   TagsPopDown();\r
8504 }\r
8505 \r
8506 \r
8507 VOID\r
8508 CommentPopUp(char *title, char *str)\r
8509 {\r
8510   HWND hwnd = GetActiveWindow();\r
8511   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8512   SAY(str);\r
8513   SetActiveWindow(hwnd);\r
8514 }\r
8515 \r
8516 VOID\r
8517 CommentPopDown(void)\r
8518 {\r
8519   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8520   if (commentDialog) {\r
8521     ShowWindow(commentDialog, SW_HIDE);\r
8522   }\r
8523   commentUp = FALSE;\r
8524 }\r
8525 \r
8526 VOID\r
8527 EditCommentPopUp(int index, char *title, char *str)\r
8528 {\r
8529   EitherCommentPopUp(index, title, str, TRUE);\r
8530 }\r
8531 \r
8532 \r
8533 VOID\r
8534 RingBell()\r
8535 {\r
8536   MyPlaySound(&sounds[(int)SoundMove]);\r
8537 }\r
8538 \r
8539 VOID PlayIcsWinSound()\r
8540 {\r
8541   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8542 }\r
8543 \r
8544 VOID PlayIcsLossSound()\r
8545 {\r
8546   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8547 }\r
8548 \r
8549 VOID PlayIcsDrawSound()\r
8550 {\r
8551   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8552 }\r
8553 \r
8554 VOID PlayIcsUnfinishedSound()\r
8555 {\r
8556   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8557 }\r
8558 \r
8559 VOID\r
8560 PlayAlarmSound()\r
8561 {\r
8562   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8563 }\r
8564 \r
8565 \r
8566 VOID\r
8567 EchoOn()\r
8568 {\r
8569   HWND hInput;\r
8570   consoleEcho = TRUE;\r
8571   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8572   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8573   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8574 }\r
8575 \r
8576 \r
8577 VOID\r
8578 EchoOff()\r
8579 {\r
8580   CHARFORMAT cf;\r
8581   HWND hInput;\r
8582   consoleEcho = FALSE;\r
8583   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8584   /* This works OK: set text and background both to the same color */\r
8585   cf = consoleCF;\r
8586   cf.crTextColor = COLOR_ECHOOFF;\r
8587   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8588   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8589 }\r
8590 \r
8591 /* No Raw()...? */\r
8592 \r
8593 void Colorize(ColorClass cc, int continuation)\r
8594 {\r
8595   currentColorClass = cc;\r
8596   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8597   consoleCF.crTextColor = textAttribs[cc].color;\r
8598   consoleCF.dwEffects = textAttribs[cc].effects;\r
8599   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8600 }\r
8601 \r
8602 char *\r
8603 UserName()\r
8604 {\r
8605   static char buf[MSG_SIZ];\r
8606   DWORD bufsiz = MSG_SIZ;\r
8607 \r
8608   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8609         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8610   }\r
8611   if (!GetUserName(buf, &bufsiz)) {\r
8612     /*DisplayError("Error getting user name", GetLastError());*/\r
8613     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8614   }\r
8615   return buf;\r
8616 }\r
8617 \r
8618 char *\r
8619 HostName()\r
8620 {\r
8621   static char buf[MSG_SIZ];\r
8622   DWORD bufsiz = MSG_SIZ;\r
8623 \r
8624   if (!GetComputerName(buf, &bufsiz)) {\r
8625     /*DisplayError("Error getting host name", GetLastError());*/\r
8626     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8627   }\r
8628   return buf;\r
8629 }\r
8630 \r
8631 \r
8632 int\r
8633 ClockTimerRunning()\r
8634 {\r
8635   return clockTimerEvent != 0;\r
8636 }\r
8637 \r
8638 int\r
8639 StopClockTimer()\r
8640 {\r
8641   if (clockTimerEvent == 0) return FALSE;\r
8642   KillTimer(hwndMain, clockTimerEvent);\r
8643   clockTimerEvent = 0;\r
8644   return TRUE;\r
8645 }\r
8646 \r
8647 void\r
8648 StartClockTimer(long millisec)\r
8649 {\r
8650   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8651                              (UINT) millisec, NULL);\r
8652 }\r
8653 \r
8654 void\r
8655 DisplayWhiteClock(long timeRemaining, int highlight)\r
8656 {\r
8657   HDC hdc;\r
8658   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8659 \r
8660   if(appData.noGUI) return;\r
8661   hdc = GetDC(hwndMain);\r
8662   if (!IsIconic(hwndMain)) {\r
8663     DisplayAClock(hdc, timeRemaining, highlight, \r
8664                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8665   }\r
8666   if (highlight && iconCurrent == iconBlack) {\r
8667     iconCurrent = iconWhite;\r
8668     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8669     if (IsIconic(hwndMain)) {\r
8670       DrawIcon(hdc, 2, 2, iconCurrent);\r
8671     }\r
8672   }\r
8673   (void) ReleaseDC(hwndMain, hdc);\r
8674   if (hwndConsole)\r
8675     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8676 }\r
8677 \r
8678 void\r
8679 DisplayBlackClock(long timeRemaining, int highlight)\r
8680 {\r
8681   HDC hdc;\r
8682   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8683 \r
8684   if(appData.noGUI) return;\r
8685   hdc = GetDC(hwndMain);\r
8686   if (!IsIconic(hwndMain)) {\r
8687     DisplayAClock(hdc, timeRemaining, highlight, \r
8688                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8689   }\r
8690   if (highlight && iconCurrent == iconWhite) {\r
8691     iconCurrent = iconBlack;\r
8692     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8693     if (IsIconic(hwndMain)) {\r
8694       DrawIcon(hdc, 2, 2, iconCurrent);\r
8695     }\r
8696   }\r
8697   (void) ReleaseDC(hwndMain, hdc);\r
8698   if (hwndConsole)\r
8699     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8700 }\r
8701 \r
8702 \r
8703 int\r
8704 LoadGameTimerRunning()\r
8705 {\r
8706   return loadGameTimerEvent != 0;\r
8707 }\r
8708 \r
8709 int\r
8710 StopLoadGameTimer()\r
8711 {\r
8712   if (loadGameTimerEvent == 0) return FALSE;\r
8713   KillTimer(hwndMain, loadGameTimerEvent);\r
8714   loadGameTimerEvent = 0;\r
8715   return TRUE;\r
8716 }\r
8717 \r
8718 void\r
8719 StartLoadGameTimer(long millisec)\r
8720 {\r
8721   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8722                                 (UINT) millisec, NULL);\r
8723 }\r
8724 \r
8725 void\r
8726 AutoSaveGame()\r
8727 {\r
8728   char *defName;\r
8729   FILE *f;\r
8730   char fileTitle[MSG_SIZ];\r
8731 \r
8732   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8733   f = OpenFileDialog(hwndMain, "a", defName,\r
8734                      appData.oldSaveStyle ? "gam" : "pgn",\r
8735                      GAME_FILT, \r
8736                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8737   if (f != NULL) {\r
8738     SaveGame(f, 0, "");\r
8739     fclose(f);\r
8740   }\r
8741 }\r
8742 \r
8743 \r
8744 void\r
8745 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8746 {\r
8747   if (delayedTimerEvent != 0) {\r
8748     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8749       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8750     }\r
8751     KillTimer(hwndMain, delayedTimerEvent);\r
8752     delayedTimerEvent = 0;\r
8753     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8754     delayedTimerCallback();\r
8755   }\r
8756   delayedTimerCallback = cb;\r
8757   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8758                                 (UINT) millisec, NULL);\r
8759 }\r
8760 \r
8761 DelayedEventCallback\r
8762 GetDelayedEvent()\r
8763 {\r
8764   if (delayedTimerEvent) {\r
8765     return delayedTimerCallback;\r
8766   } else {\r
8767     return NULL;\r
8768   }\r
8769 }\r
8770 \r
8771 void\r
8772 CancelDelayedEvent()\r
8773 {\r
8774   if (delayedTimerEvent) {\r
8775     KillTimer(hwndMain, delayedTimerEvent);\r
8776     delayedTimerEvent = 0;\r
8777   }\r
8778 }\r
8779 \r
8780 DWORD GetWin32Priority(int nice)\r
8781 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8782 /*\r
8783 REALTIME_PRIORITY_CLASS     0x00000100\r
8784 HIGH_PRIORITY_CLASS         0x00000080\r
8785 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8786 NORMAL_PRIORITY_CLASS       0x00000020\r
8787 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8788 IDLE_PRIORITY_CLASS         0x00000040\r
8789 */\r
8790         if (nice < -15) return 0x00000080;\r
8791         if (nice < 0)   return 0x00008000;\r
8792         if (nice == 0)  return 0x00000020;\r
8793         if (nice < 15)  return 0x00004000;\r
8794         return 0x00000040;\r
8795 }\r
8796 \r
8797 /* Start a child process running the given program.\r
8798    The process's standard output can be read from "from", and its\r
8799    standard input can be written to "to".\r
8800    Exit with fatal error if anything goes wrong.\r
8801    Returns an opaque pointer that can be used to destroy the process\r
8802    later.\r
8803 */\r
8804 int\r
8805 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8806 {\r
8807 #define BUFSIZE 4096\r
8808 \r
8809   HANDLE hChildStdinRd, hChildStdinWr,\r
8810     hChildStdoutRd, hChildStdoutWr;\r
8811   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8812   SECURITY_ATTRIBUTES saAttr;\r
8813   BOOL fSuccess;\r
8814   PROCESS_INFORMATION piProcInfo;\r
8815   STARTUPINFO siStartInfo;\r
8816   ChildProc *cp;\r
8817   char buf[MSG_SIZ];\r
8818   DWORD err;\r
8819 \r
8820   if (appData.debugMode) {\r
8821     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8822   }\r
8823 \r
8824   *pr = NoProc;\r
8825 \r
8826   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8827   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8828   saAttr.bInheritHandle = TRUE;\r
8829   saAttr.lpSecurityDescriptor = NULL;\r
8830 \r
8831   /*\r
8832    * The steps for redirecting child's STDOUT:\r
8833    *     1. Create anonymous pipe to be STDOUT for child.\r
8834    *     2. Create a noninheritable duplicate of read handle,\r
8835    *         and close the inheritable read handle.\r
8836    */\r
8837 \r
8838   /* Create a pipe for the child's STDOUT. */\r
8839   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8840     return GetLastError();\r
8841   }\r
8842 \r
8843   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8844   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8845                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8846                              FALSE,     /* not inherited */\r
8847                              DUPLICATE_SAME_ACCESS);\r
8848   if (! fSuccess) {\r
8849     return GetLastError();\r
8850   }\r
8851   CloseHandle(hChildStdoutRd);\r
8852 \r
8853   /*\r
8854    * The steps for redirecting child's STDIN:\r
8855    *     1. Create anonymous pipe to be STDIN for child.\r
8856    *     2. Create a noninheritable duplicate of write handle,\r
8857    *         and close the inheritable write handle.\r
8858    */\r
8859 \r
8860   /* Create a pipe for the child's STDIN. */\r
8861   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8862     return GetLastError();\r
8863   }\r
8864 \r
8865   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8866   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8867                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8868                              FALSE,     /* not inherited */\r
8869                              DUPLICATE_SAME_ACCESS);\r
8870   if (! fSuccess) {\r
8871     return GetLastError();\r
8872   }\r
8873   CloseHandle(hChildStdinWr);\r
8874 \r
8875   /* Arrange to (1) look in dir for the child .exe file, and\r
8876    * (2) have dir be the child's working directory.  Interpret\r
8877    * dir relative to the directory WinBoard loaded from. */\r
8878   GetCurrentDirectory(MSG_SIZ, buf);\r
8879   SetCurrentDirectory(installDir);\r
8880   SetCurrentDirectory(dir);\r
8881 \r
8882   /* Now create the child process. */\r
8883 \r
8884   siStartInfo.cb = sizeof(STARTUPINFO);\r
8885   siStartInfo.lpReserved = NULL;\r
8886   siStartInfo.lpDesktop = NULL;\r
8887   siStartInfo.lpTitle = NULL;\r
8888   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8889   siStartInfo.cbReserved2 = 0;\r
8890   siStartInfo.lpReserved2 = NULL;\r
8891   siStartInfo.hStdInput = hChildStdinRd;\r
8892   siStartInfo.hStdOutput = hChildStdoutWr;\r
8893   siStartInfo.hStdError = hChildStdoutWr;\r
8894 \r
8895   fSuccess = CreateProcess(NULL,\r
8896                            cmdLine,        /* command line */\r
8897                            NULL,           /* process security attributes */\r
8898                            NULL,           /* primary thread security attrs */\r
8899                            TRUE,           /* handles are inherited */\r
8900                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8901                            NULL,           /* use parent's environment */\r
8902                            NULL,\r
8903                            &siStartInfo, /* STARTUPINFO pointer */\r
8904                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8905 \r
8906   err = GetLastError();\r
8907   SetCurrentDirectory(buf); /* return to prev directory */\r
8908   if (! fSuccess) {\r
8909     return err;\r
8910   }\r
8911 \r
8912   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8913     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8914     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8915   }\r
8916 \r
8917   /* Close the handles we don't need in the parent */\r
8918   CloseHandle(piProcInfo.hThread);\r
8919   CloseHandle(hChildStdinRd);\r
8920   CloseHandle(hChildStdoutWr);\r
8921 \r
8922   /* Prepare return value */\r
8923   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8924   cp->kind = CPReal;\r
8925   cp->hProcess = piProcInfo.hProcess;\r
8926   cp->pid = piProcInfo.dwProcessId;\r
8927   cp->hFrom = hChildStdoutRdDup;\r
8928   cp->hTo = hChildStdinWrDup;\r
8929 \r
8930   *pr = (void *) cp;\r
8931 \r
8932   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8933      2000 where engines sometimes don't see the initial command(s)\r
8934      from WinBoard and hang.  I don't understand how that can happen,\r
8935      but the Sleep is harmless, so I've put it in.  Others have also\r
8936      reported what may be the same problem, so hopefully this will fix\r
8937      it for them too.  */\r
8938   Sleep(500);\r
8939 \r
8940   return NO_ERROR;\r
8941 }\r
8942 \r
8943 \r
8944 void\r
8945 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8946 {\r
8947   ChildProc *cp; int result;\r
8948 \r
8949   cp = (ChildProc *) pr;\r
8950   if (cp == NULL) return;\r
8951 \r
8952   switch (cp->kind) {\r
8953   case CPReal:\r
8954     /* TerminateProcess is considered harmful, so... */\r
8955     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8956     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8957     /* The following doesn't work because the chess program\r
8958        doesn't "have the same console" as WinBoard.  Maybe\r
8959        we could arrange for this even though neither WinBoard\r
8960        nor the chess program uses a console for stdio? */\r
8961     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8962 \r
8963     /* [AS] Special termination modes for misbehaving programs... */\r
8964     if( signal == 9 ) { \r
8965         result = TerminateProcess( cp->hProcess, 0 );\r
8966 \r
8967         if ( appData.debugMode) {\r
8968             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8969         }\r
8970     }\r
8971     else if( signal == 10 ) {\r
8972         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8973 \r
8974         if( dw != WAIT_OBJECT_0 ) {\r
8975             result = TerminateProcess( cp->hProcess, 0 );\r
8976 \r
8977             if ( appData.debugMode) {\r
8978                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8979             }\r
8980 \r
8981         }\r
8982     }\r
8983 \r
8984     CloseHandle(cp->hProcess);\r
8985     break;\r
8986 \r
8987   case CPComm:\r
8988     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8989     break;\r
8990 \r
8991   case CPSock:\r
8992     closesocket(cp->sock);\r
8993     WSACleanup();\r
8994     break;\r
8995 \r
8996   case CPRcmd:\r
8997     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8998     closesocket(cp->sock);\r
8999     closesocket(cp->sock2);\r
9000     WSACleanup();\r
9001     break;\r
9002   }\r
9003   free(cp);\r
9004 }\r
9005 \r
9006 void\r
9007 InterruptChildProcess(ProcRef pr)\r
9008 {\r
9009   ChildProc *cp;\r
9010 \r
9011   cp = (ChildProc *) pr;\r
9012   if (cp == NULL) return;\r
9013   switch (cp->kind) {\r
9014   case CPReal:\r
9015     /* The following doesn't work because the chess program\r
9016        doesn't "have the same console" as WinBoard.  Maybe\r
9017        we could arrange for this even though neither WinBoard\r
9018        nor the chess program uses a console for stdio */\r
9019     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9020     break;\r
9021 \r
9022   case CPComm:\r
9023   case CPSock:\r
9024     /* Can't interrupt */\r
9025     break;\r
9026 \r
9027   case CPRcmd:\r
9028     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9029     break;\r
9030   }\r
9031 }\r
9032 \r
9033 \r
9034 int\r
9035 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9036 {\r
9037   char cmdLine[MSG_SIZ];\r
9038 \r
9039   if (port[0] == NULLCHAR) {\r
9040     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9041   } else {\r
9042     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9043   }\r
9044   return StartChildProcess(cmdLine, "", pr);\r
9045 }\r
9046 \r
9047 \r
9048 /* Code to open TCP sockets */\r
9049 \r
9050 int\r
9051 OpenTCP(char *host, char *port, ProcRef *pr)\r
9052 {\r
9053   ChildProc *cp;\r
9054   int err;\r
9055   SOCKET s;\r
9056   struct sockaddr_in sa, mysa;\r
9057   struct hostent FAR *hp;\r
9058   unsigned short uport;\r
9059   WORD wVersionRequested;\r
9060   WSADATA wsaData;\r
9061 \r
9062   /* Initialize socket DLL */\r
9063   wVersionRequested = MAKEWORD(1, 1);\r
9064   err = WSAStartup(wVersionRequested, &wsaData);\r
9065   if (err != 0) return err;\r
9066 \r
9067   /* Make socket */\r
9068   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9069     err = WSAGetLastError();\r
9070     WSACleanup();\r
9071     return err;\r
9072   }\r
9073 \r
9074   /* Bind local address using (mostly) don't-care values.\r
9075    */\r
9076   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9077   mysa.sin_family = AF_INET;\r
9078   mysa.sin_addr.s_addr = INADDR_ANY;\r
9079   uport = (unsigned short) 0;\r
9080   mysa.sin_port = htons(uport);\r
9081   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9082       == SOCKET_ERROR) {\r
9083     err = WSAGetLastError();\r
9084     WSACleanup();\r
9085     return err;\r
9086   }\r
9087 \r
9088   /* Resolve remote host name */\r
9089   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9090   if (!(hp = gethostbyname(host))) {\r
9091     unsigned int b0, b1, b2, b3;\r
9092 \r
9093     err = WSAGetLastError();\r
9094 \r
9095     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9096       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9097       hp->h_addrtype = AF_INET;\r
9098       hp->h_length = 4;\r
9099       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9100       hp->h_addr_list[0] = (char *) malloc(4);\r
9101       hp->h_addr_list[0][0] = (char) b0;\r
9102       hp->h_addr_list[0][1] = (char) b1;\r
9103       hp->h_addr_list[0][2] = (char) b2;\r
9104       hp->h_addr_list[0][3] = (char) b3;\r
9105     } else {\r
9106       WSACleanup();\r
9107       return err;\r
9108     }\r
9109   }\r
9110   sa.sin_family = hp->h_addrtype;\r
9111   uport = (unsigned short) atoi(port);\r
9112   sa.sin_port = htons(uport);\r
9113   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9114 \r
9115   /* Make connection */\r
9116   if (connect(s, (struct sockaddr *) &sa,\r
9117               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9118     err = WSAGetLastError();\r
9119     WSACleanup();\r
9120     return err;\r
9121   }\r
9122 \r
9123   /* Prepare return value */\r
9124   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9125   cp->kind = CPSock;\r
9126   cp->sock = s;\r
9127   *pr = (ProcRef *) cp;\r
9128 \r
9129   return NO_ERROR;\r
9130 }\r
9131 \r
9132 int\r
9133 OpenCommPort(char *name, ProcRef *pr)\r
9134 {\r
9135   HANDLE h;\r
9136   COMMTIMEOUTS ct;\r
9137   ChildProc *cp;\r
9138   char fullname[MSG_SIZ];\r
9139 \r
9140   if (*name != '\\')\r
9141     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9142   else\r
9143     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9144 \r
9145   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9146                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9147   if (h == (HANDLE) -1) {\r
9148     return GetLastError();\r
9149   }\r
9150   hCommPort = h;\r
9151 \r
9152   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9153 \r
9154   /* Accumulate characters until a 100ms pause, then parse */\r
9155   ct.ReadIntervalTimeout = 100;\r
9156   ct.ReadTotalTimeoutMultiplier = 0;\r
9157   ct.ReadTotalTimeoutConstant = 0;\r
9158   ct.WriteTotalTimeoutMultiplier = 0;\r
9159   ct.WriteTotalTimeoutConstant = 0;\r
9160   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9161 \r
9162   /* Prepare return value */\r
9163   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9164   cp->kind = CPComm;\r
9165   cp->hFrom = h;\r
9166   cp->hTo = h;\r
9167   *pr = (ProcRef *) cp;\r
9168 \r
9169   return NO_ERROR;\r
9170 }\r
9171 \r
9172 int\r
9173 OpenLoopback(ProcRef *pr)\r
9174 {\r
9175   DisplayFatalError(_("Not implemented"), 0, 1);\r
9176   return NO_ERROR;\r
9177 }\r
9178 \r
9179 \r
9180 int\r
9181 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9182 {\r
9183   ChildProc *cp;\r
9184   int err;\r
9185   SOCKET s, s2, s3;\r
9186   struct sockaddr_in sa, mysa;\r
9187   struct hostent FAR *hp;\r
9188   unsigned short uport;\r
9189   WORD wVersionRequested;\r
9190   WSADATA wsaData;\r
9191   int fromPort;\r
9192   char stderrPortStr[MSG_SIZ];\r
9193 \r
9194   /* Initialize socket DLL */\r
9195   wVersionRequested = MAKEWORD(1, 1);\r
9196   err = WSAStartup(wVersionRequested, &wsaData);\r
9197   if (err != 0) return err;\r
9198 \r
9199   /* Resolve remote host name */\r
9200   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9201   if (!(hp = gethostbyname(host))) {\r
9202     unsigned int b0, b1, b2, b3;\r
9203 \r
9204     err = WSAGetLastError();\r
9205 \r
9206     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9207       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9208       hp->h_addrtype = AF_INET;\r
9209       hp->h_length = 4;\r
9210       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9211       hp->h_addr_list[0] = (char *) malloc(4);\r
9212       hp->h_addr_list[0][0] = (char) b0;\r
9213       hp->h_addr_list[0][1] = (char) b1;\r
9214       hp->h_addr_list[0][2] = (char) b2;\r
9215       hp->h_addr_list[0][3] = (char) b3;\r
9216     } else {\r
9217       WSACleanup();\r
9218       return err;\r
9219     }\r
9220   }\r
9221   sa.sin_family = hp->h_addrtype;\r
9222   uport = (unsigned short) 514;\r
9223   sa.sin_port = htons(uport);\r
9224   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9225 \r
9226   /* Bind local socket to unused "privileged" port address\r
9227    */\r
9228   s = INVALID_SOCKET;\r
9229   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9230   mysa.sin_family = AF_INET;\r
9231   mysa.sin_addr.s_addr = INADDR_ANY;\r
9232   for (fromPort = 1023;; fromPort--) {\r
9233     if (fromPort < 0) {\r
9234       WSACleanup();\r
9235       return WSAEADDRINUSE;\r
9236     }\r
9237     if (s == INVALID_SOCKET) {\r
9238       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9239         err = WSAGetLastError();\r
9240         WSACleanup();\r
9241         return err;\r
9242       }\r
9243     }\r
9244     uport = (unsigned short) fromPort;\r
9245     mysa.sin_port = htons(uport);\r
9246     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9247         == SOCKET_ERROR) {\r
9248       err = WSAGetLastError();\r
9249       if (err == WSAEADDRINUSE) continue;\r
9250       WSACleanup();\r
9251       return err;\r
9252     }\r
9253     if (connect(s, (struct sockaddr *) &sa,\r
9254       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9255       err = WSAGetLastError();\r
9256       if (err == WSAEADDRINUSE) {\r
9257         closesocket(s);\r
9258         s = -1;\r
9259         continue;\r
9260       }\r
9261       WSACleanup();\r
9262       return err;\r
9263     }\r
9264     break;\r
9265   }\r
9266 \r
9267   /* Bind stderr local socket to unused "privileged" port address\r
9268    */\r
9269   s2 = INVALID_SOCKET;\r
9270   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9271   mysa.sin_family = AF_INET;\r
9272   mysa.sin_addr.s_addr = INADDR_ANY;\r
9273   for (fromPort = 1023;; fromPort--) {\r
9274     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9275     if (fromPort < 0) {\r
9276       (void) closesocket(s);\r
9277       WSACleanup();\r
9278       return WSAEADDRINUSE;\r
9279     }\r
9280     if (s2 == INVALID_SOCKET) {\r
9281       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9282         err = WSAGetLastError();\r
9283         closesocket(s);\r
9284         WSACleanup();\r
9285         return err;\r
9286       }\r
9287     }\r
9288     uport = (unsigned short) fromPort;\r
9289     mysa.sin_port = htons(uport);\r
9290     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9291         == SOCKET_ERROR) {\r
9292       err = WSAGetLastError();\r
9293       if (err == WSAEADDRINUSE) continue;\r
9294       (void) closesocket(s);\r
9295       WSACleanup();\r
9296       return err;\r
9297     }\r
9298     if (listen(s2, 1) == SOCKET_ERROR) {\r
9299       err = WSAGetLastError();\r
9300       if (err == WSAEADDRINUSE) {\r
9301         closesocket(s2);\r
9302         s2 = INVALID_SOCKET;\r
9303         continue;\r
9304       }\r
9305       (void) closesocket(s);\r
9306       (void) closesocket(s2);\r
9307       WSACleanup();\r
9308       return err;\r
9309     }\r
9310     break;\r
9311   }\r
9312   prevStderrPort = fromPort; // remember port used\r
9313   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9314 \r
9315   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9316     err = WSAGetLastError();\r
9317     (void) closesocket(s);\r
9318     (void) closesocket(s2);\r
9319     WSACleanup();\r
9320     return err;\r
9321   }\r
9322 \r
9323   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9324     err = WSAGetLastError();\r
9325     (void) closesocket(s);\r
9326     (void) closesocket(s2);\r
9327     WSACleanup();\r
9328     return err;\r
9329   }\r
9330   if (*user == NULLCHAR) user = UserName();\r
9331   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9332     err = WSAGetLastError();\r
9333     (void) closesocket(s);\r
9334     (void) closesocket(s2);\r
9335     WSACleanup();\r
9336     return err;\r
9337   }\r
9338   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9339     err = WSAGetLastError();\r
9340     (void) closesocket(s);\r
9341     (void) closesocket(s2);\r
9342     WSACleanup();\r
9343     return err;\r
9344   }\r
9345 \r
9346   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9347     err = WSAGetLastError();\r
9348     (void) closesocket(s);\r
9349     (void) closesocket(s2);\r
9350     WSACleanup();\r
9351     return err;\r
9352   }\r
9353   (void) closesocket(s2);  /* Stop listening */\r
9354 \r
9355   /* Prepare return value */\r
9356   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9357   cp->kind = CPRcmd;\r
9358   cp->sock = s;\r
9359   cp->sock2 = s3;\r
9360   *pr = (ProcRef *) cp;\r
9361 \r
9362   return NO_ERROR;\r
9363 }\r
9364 \r
9365 \r
9366 InputSourceRef\r
9367 AddInputSource(ProcRef pr, int lineByLine,\r
9368                InputCallback func, VOIDSTAR closure)\r
9369 {\r
9370   InputSource *is, *is2 = NULL;\r
9371   ChildProc *cp = (ChildProc *) pr;\r
9372 \r
9373   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9374   is->lineByLine = lineByLine;\r
9375   is->func = func;\r
9376   is->closure = closure;\r
9377   is->second = NULL;\r
9378   is->next = is->buf;\r
9379   if (pr == NoProc) {\r
9380     is->kind = CPReal;\r
9381     consoleInputSource = is;\r
9382   } else {\r
9383     is->kind = cp->kind;\r
9384     /* \r
9385         [AS] Try to avoid a race condition if the thread is given control too early:\r
9386         we create all threads suspended so that the is->hThread variable can be\r
9387         safely assigned, then let the threads start with ResumeThread.\r
9388     */\r
9389     switch (cp->kind) {\r
9390     case CPReal:\r
9391       is->hFile = cp->hFrom;\r
9392       cp->hFrom = NULL; /* now owned by InputThread */\r
9393       is->hThread =\r
9394         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9395                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9396       break;\r
9397 \r
9398     case CPComm:\r
9399       is->hFile = cp->hFrom;\r
9400       cp->hFrom = NULL; /* now owned by InputThread */\r
9401       is->hThread =\r
9402         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9403                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9404       break;\r
9405 \r
9406     case CPSock:\r
9407       is->sock = cp->sock;\r
9408       is->hThread =\r
9409         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9410                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9411       break;\r
9412 \r
9413     case CPRcmd:\r
9414       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9415       *is2 = *is;\r
9416       is->sock = cp->sock;\r
9417       is->second = is2;\r
9418       is2->sock = cp->sock2;\r
9419       is2->second = is2;\r
9420       is->hThread =\r
9421         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9422                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9423       is2->hThread =\r
9424         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9425                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9426       break;\r
9427     }\r
9428 \r
9429     if( is->hThread != NULL ) {\r
9430         ResumeThread( is->hThread );\r
9431     }\r
9432 \r
9433     if( is2 != NULL && is2->hThread != NULL ) {\r
9434         ResumeThread( is2->hThread );\r
9435     }\r
9436   }\r
9437 \r
9438   return (InputSourceRef) is;\r
9439 }\r
9440 \r
9441 void\r
9442 RemoveInputSource(InputSourceRef isr)\r
9443 {\r
9444   InputSource *is;\r
9445 \r
9446   is = (InputSource *) isr;\r
9447   is->hThread = NULL;  /* tell thread to stop */\r
9448   CloseHandle(is->hThread);\r
9449   if (is->second != NULL) {\r
9450     is->second->hThread = NULL;\r
9451     CloseHandle(is->second->hThread);\r
9452   }\r
9453 }\r
9454 \r
9455 int no_wrap(char *message, int count)\r
9456 {\r
9457     ConsoleOutput(message, count, FALSE);\r
9458     return count;\r
9459 }\r
9460 \r
9461 int\r
9462 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9463 {\r
9464   DWORD dOutCount;\r
9465   int outCount = SOCKET_ERROR;\r
9466   ChildProc *cp = (ChildProc *) pr;\r
9467   static OVERLAPPED ovl;\r
9468   static int line = 0;\r
9469 \r
9470   if (pr == NoProc)\r
9471   {\r
9472     if (appData.noJoin || !appData.useInternalWrap)\r
9473       return no_wrap(message, count);\r
9474     else\r
9475     {\r
9476       int width = get_term_width();\r
9477       int len = wrap(NULL, message, count, width, &line);\r
9478       char *msg = malloc(len);\r
9479       int dbgchk;\r
9480 \r
9481       if (!msg)\r
9482         return no_wrap(message, count);\r
9483       else\r
9484       {\r
9485         dbgchk = wrap(msg, message, count, width, &line);\r
9486         if (dbgchk != len && appData.debugMode)\r
9487             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9488         ConsoleOutput(msg, len, FALSE);\r
9489         free(msg);\r
9490         return len;\r
9491       }\r
9492     }\r
9493   }\r
9494 \r
9495   if (ovl.hEvent == NULL) {\r
9496     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9497   }\r
9498   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9499 \r
9500   switch (cp->kind) {\r
9501   case CPSock:\r
9502   case CPRcmd:\r
9503     outCount = send(cp->sock, message, count, 0);\r
9504     if (outCount == SOCKET_ERROR) {\r
9505       *outError = WSAGetLastError();\r
9506     } else {\r
9507       *outError = NO_ERROR;\r
9508     }\r
9509     break;\r
9510 \r
9511   case CPReal:\r
9512     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9513                   &dOutCount, NULL)) {\r
9514       *outError = NO_ERROR;\r
9515       outCount = (int) dOutCount;\r
9516     } else {\r
9517       *outError = GetLastError();\r
9518     }\r
9519     break;\r
9520 \r
9521   case CPComm:\r
9522     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9523                             &dOutCount, &ovl);\r
9524     if (*outError == NO_ERROR) {\r
9525       outCount = (int) dOutCount;\r
9526     }\r
9527     break;\r
9528   }\r
9529   return outCount;\r
9530 }\r
9531 \r
9532 int\r
9533 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9534                        long msdelay)\r
9535 {\r
9536   /* Ignore delay, not implemented for WinBoard */\r
9537   return OutputToProcess(pr, message, count, outError);\r
9538 }\r
9539 \r
9540 \r
9541 void\r
9542 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9543                         char *buf, int count, int error)\r
9544 {\r
9545   DisplayFatalError(_("Not implemented"), 0, 1);\r
9546 }\r
9547 \r
9548 /* see wgamelist.c for Game List functions */\r
9549 /* see wedittags.c for Edit Tags functions */\r
9550 \r
9551 \r
9552 VOID\r
9553 ICSInitScript()\r
9554 {\r
9555   FILE *f;\r
9556   char buf[MSG_SIZ];\r
9557   char *dummy;\r
9558 \r
9559   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9560     f = fopen(buf, "r");\r
9561     if (f != NULL) {\r
9562       ProcessICSInitScript(f);\r
9563       fclose(f);\r
9564     }\r
9565   }\r
9566 }\r
9567 \r
9568 \r
9569 VOID\r
9570 StartAnalysisClock()\r
9571 {\r
9572   if (analysisTimerEvent) return;\r
9573   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9574                                         (UINT) 2000, NULL);\r
9575 }\r
9576 \r
9577 VOID\r
9578 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9579 {\r
9580   highlightInfo.sq[0].x = fromX;\r
9581   highlightInfo.sq[0].y = fromY;\r
9582   highlightInfo.sq[1].x = toX;\r
9583   highlightInfo.sq[1].y = toY;\r
9584 }\r
9585 \r
9586 VOID\r
9587 ClearHighlights()\r
9588 {\r
9589   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9590     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9591 }\r
9592 \r
9593 VOID\r
9594 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9595 {\r
9596   premoveHighlightInfo.sq[0].x = fromX;\r
9597   premoveHighlightInfo.sq[0].y = fromY;\r
9598   premoveHighlightInfo.sq[1].x = toX;\r
9599   premoveHighlightInfo.sq[1].y = toY;\r
9600 }\r
9601 \r
9602 VOID\r
9603 ClearPremoveHighlights()\r
9604 {\r
9605   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9606     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9607 }\r
9608 \r
9609 VOID\r
9610 ShutDownFrontEnd()\r
9611 {\r
9612   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9613   DeleteClipboardTempFiles();\r
9614 }\r
9615 \r
9616 void\r
9617 BoardToTop()\r
9618 {\r
9619     if (IsIconic(hwndMain))\r
9620       ShowWindow(hwndMain, SW_RESTORE);\r
9621 \r
9622     SetActiveWindow(hwndMain);\r
9623 }\r
9624 \r
9625 /*\r
9626  * Prototypes for animation support routines\r
9627  */\r
9628 static void ScreenSquare(int column, int row, POINT * pt);\r
9629 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9630      POINT frames[], int * nFrames);\r
9631 \r
9632 \r
9633 #define kFactor 4\r
9634 \r
9635 void\r
9636 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9637 {       // [HGM] atomic: animate blast wave\r
9638         int i;\r
9639 \r
9640         explodeInfo.fromX = fromX;\r
9641         explodeInfo.fromY = fromY;\r
9642         explodeInfo.toX = toX;\r
9643         explodeInfo.toY = toY;\r
9644         for(i=1; i<4*kFactor; i++) {\r
9645             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9646             DrawPosition(FALSE, board);\r
9647             Sleep(appData.animSpeed);\r
9648         }\r
9649         explodeInfo.radius = 0;\r
9650         DrawPosition(TRUE, board);\r
9651 }\r
9652 \r
9653 void\r
9654 AnimateMove(board, fromX, fromY, toX, toY)\r
9655      Board board;\r
9656      int fromX;\r
9657      int fromY;\r
9658      int toX;\r
9659      int toY;\r
9660 {\r
9661   ChessSquare piece;\r
9662   POINT start, finish, mid;\r
9663   POINT frames[kFactor * 2 + 1];\r
9664   int nFrames, n;\r
9665 \r
9666   if (!appData.animate) return;\r
9667   if (doingSizing) return;\r
9668   if (fromY < 0 || fromX < 0) return;\r
9669   piece = board[fromY][fromX];\r
9670   if (piece >= EmptySquare) return;\r
9671 \r
9672   ScreenSquare(fromX, fromY, &start);\r
9673   ScreenSquare(toX, toY, &finish);\r
9674 \r
9675   /* All moves except knight jumps move in straight line */\r
9676   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9677     mid.x = start.x + (finish.x - start.x) / 2;\r
9678     mid.y = start.y + (finish.y - start.y) / 2;\r
9679   } else {\r
9680     /* Knight: make straight movement then diagonal */\r
9681     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9682        mid.x = start.x + (finish.x - start.x) / 2;\r
9683        mid.y = start.y;\r
9684      } else {\r
9685        mid.x = start.x;\r
9686        mid.y = start.y + (finish.y - start.y) / 2;\r
9687      }\r
9688   }\r
9689   \r
9690   /* Don't use as many frames for very short moves */\r
9691   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9692     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9693   else\r
9694     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9695 \r
9696   animInfo.from.x = fromX;\r
9697   animInfo.from.y = fromY;\r
9698   animInfo.to.x = toX;\r
9699   animInfo.to.y = toY;\r
9700   animInfo.lastpos = start;\r
9701   animInfo.piece = piece;\r
9702   for (n = 0; n < nFrames; n++) {\r
9703     animInfo.pos = frames[n];\r
9704     DrawPosition(FALSE, NULL);\r
9705     animInfo.lastpos = animInfo.pos;\r
9706     Sleep(appData.animSpeed);\r
9707   }\r
9708   animInfo.pos = finish;\r
9709   DrawPosition(FALSE, NULL);\r
9710   animInfo.piece = EmptySquare;\r
9711   Explode(board, fromX, fromY, toX, toY);\r
9712 }\r
9713 \r
9714 /*      Convert board position to corner of screen rect and color       */\r
9715 \r
9716 static void\r
9717 ScreenSquare(column, row, pt)\r
9718      int column; int row; POINT * pt;\r
9719 {\r
9720   if (flipView) {\r
9721     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9722     pt->y = lineGap + row * (squareSize + lineGap);\r
9723   } else {\r
9724     pt->x = lineGap + column * (squareSize + lineGap);\r
9725     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9726   }\r
9727 }\r
9728 \r
9729 /*      Generate a series of frame coords from start->mid->finish.\r
9730         The movement rate doubles until the half way point is\r
9731         reached, then halves back down to the final destination,\r
9732         which gives a nice slow in/out effect. The algorithmn\r
9733         may seem to generate too many intermediates for short\r
9734         moves, but remember that the purpose is to attract the\r
9735         viewers attention to the piece about to be moved and\r
9736         then to where it ends up. Too few frames would be less\r
9737         noticeable.                                             */\r
9738 \r
9739 static void\r
9740 Tween(start, mid, finish, factor, frames, nFrames)\r
9741      POINT * start; POINT * mid;\r
9742      POINT * finish; int factor;\r
9743      POINT frames[]; int * nFrames;\r
9744 {\r
9745   int n, fraction = 1, count = 0;\r
9746 \r
9747   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9748   for (n = 0; n < factor; n++)\r
9749     fraction *= 2;\r
9750   for (n = 0; n < factor; n++) {\r
9751     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9752     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9753     count ++;\r
9754     fraction = fraction / 2;\r
9755   }\r
9756   \r
9757   /* Midpoint */\r
9758   frames[count] = *mid;\r
9759   count ++;\r
9760   \r
9761   /* Slow out, stepping 1/2, then 1/4, ... */\r
9762   fraction = 2;\r
9763   for (n = 0; n < factor; n++) {\r
9764     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9765     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9766     count ++;\r
9767     fraction = fraction * 2;\r
9768   }\r
9769   *nFrames = count;\r
9770 }\r
9771 \r
9772 void\r
9773 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9774 {\r
9775     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9776 \r
9777     EvalGraphSet( first, last, current, pvInfoList );\r
9778 }\r
9779 \r
9780 void\r
9781 SettingsPopUp(ChessProgramState *cps)\r
9782 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9783       EngineOptionsPopup(savedHwnd, cps);\r
9784 }\r
9785 \r
9786 int flock(int fid, int code)\r
9787 {\r
9788     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9789     OVERLAPPED ov;\r
9790     ov.hEvent = NULL;\r
9791     ov.Offset = 0;\r
9792     ov.OffsetHigh = 0;\r
9793     switch(code) {\r
9794       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9795       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9796       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9797       default: return -1;\r
9798     }\r
9799     return 0;\r
9800 }\r