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