Make book-edit function WB
[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 char homeDir[MSG_SIZ];\r
160 int errorExitStatus;\r
161 \r
162 BoardSize boardSize;\r
163 Boolean chessProgram;\r
164 //static int boardX, boardY;\r
165 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
166 int squareSize, lineGap, minorSize;\r
167 static int winW, winH;\r
168 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
169 static int logoHeight = 0;\r
170 static char messageText[MESSAGE_TEXT_MAX];\r
171 static int clockTimerEvent = 0;\r
172 static int loadGameTimerEvent = 0;\r
173 static int analysisTimerEvent = 0;\r
174 static DelayedEventCallback delayedTimerCallback;\r
175 static int delayedTimerEvent = 0;\r
176 static int buttonCount = 2;\r
177 char *icsTextMenuString;\r
178 char *icsNames;\r
179 char *firstChessProgramNames;\r
180 char *secondChessProgramNames;\r
181 \r
182 #define PALETTESIZE 256\r
183 \r
184 HINSTANCE hInst;          /* current instance */\r
185 Boolean alwaysOnTop = FALSE;\r
186 RECT boardRect;\r
187 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
188   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
189 HPALETTE hPal;\r
190 ColorClass currentColorClass;\r
191 \r
192 static HWND savedHwnd;\r
193 HWND hCommPort = NULL;    /* currently open comm port */\r
194 static HWND hwndPause;    /* pause button */\r
195 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
196 static HBRUSH lightSquareBrush, darkSquareBrush,\r
197   blackSquareBrush, /* [HGM] for band between board and holdings */\r
198   explodeBrush,     /* [HGM] atomic */\r
199   markerBrush,      /* [HGM] markers */\r
200   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
201 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
202 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
203 static HPEN gridPen = NULL;\r
204 static HPEN highlightPen = NULL;\r
205 static HPEN premovePen = NULL;\r
206 static NPLOGPALETTE pLogPal;\r
207 static BOOL paletteChanged = FALSE;\r
208 static HICON iconWhite, iconBlack, iconCurrent;\r
209 static int doingSizing = FALSE;\r
210 static int lastSizing = 0;\r
211 static int prevStderrPort;\r
212 static HBITMAP userLogo;\r
213 \r
214 static HBITMAP liteBackTexture = NULL;\r
215 static HBITMAP darkBackTexture = NULL;\r
216 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int backTextureSquareSize = 0;\r
219 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
220 \r
221 #if __GNUC__ && !defined(_winmajor)\r
222 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
223 #else\r
224 #if defined(_winmajor)\r
225 #define oldDialog (_winmajor < 4)\r
226 #else\r
227 #define oldDialog 0\r
228 #endif\r
229 #endif\r
230 \r
231 #define INTERNATIONAL\r
232 \r
233 #ifdef INTERNATIONAL\r
234 #  define _(s) T_(s)\r
235 #  define N_(s) s\r
236 #else\r
237 #  define _(s) s\r
238 #  define N_(s) s\r
239 #  define T_(s) s\r
240 #  define Translate(x, y)\r
241 #  define LoadLanguageFile(s)\r
242 #endif\r
243 \r
244 #ifdef INTERNATIONAL\r
245 \r
246 Boolean barbaric; // flag indicating if translation is needed\r
247 \r
248 // list of item numbers used in each dialog (used to alter language at run time)\r
249 \r
250 #define ABOUTBOX -1  /* not sure why these are needed */\r
251 #define ABOUTBOX2 -1\r
252 \r
253 int dialogItems[][40] = {\r
254 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
255 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
256   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
257 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
259   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
262   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
265   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
266 { ABOUTBOX2, IDC_ChessBoard }, \r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
268   OPT_GameListClose, IDC_GameListDoFilter }, \r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
270 { DLG_Error, IDOK }, \r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
272   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
275   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
276   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
277 { DLG_IndexNumber, IDC_Index }, \r
278 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
279 { DLG_TypeInName, IDOK, IDCANCEL }, \r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
281   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
283   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
284   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
285   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
286   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
287   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
288   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
290   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
291   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
292   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
293   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
294   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
295   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
296   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
297   GPB_General, GPB_Alarm }, \r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
299   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
300   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
301   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
302   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
303   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
304   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
305   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
307   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
308   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
309   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
310   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
311   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
312   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
313   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
314   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
316   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
317   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
318   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
319 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
320 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
321   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
322 { DLG_MoveHistory }, \r
323 { DLG_EvalGraph }, \r
324 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
325 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
326 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
327   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
328   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
329   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
330 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
331   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
332   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
333 { 0 }\r
334 };\r
335 \r
336 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
337 static int lastChecked;\r
338 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
339 extern int tinyLayout;\r
340 extern char * menuBarText[][10];\r
341 \r
342 void\r
343 LoadLanguageFile(char *name)\r
344 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
345     FILE *f;\r
346     int i=0, j=0, n=0, k;\r
347     char buf[MSG_SIZ];\r
348 \r
349     if(!name || name[0] == NULLCHAR) return;\r
350       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
351     appData.language = oldLanguage;\r
352     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
353     if((f = fopen(buf, "r")) == NULL) return;\r
354     while((k = fgetc(f)) != EOF) {\r
355         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
356         languageBuf[i] = k;\r
357         if(k == '\n') {\r
358             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
359                 char *p;\r
360                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
361                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
362                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
363                         english[j] = languageBuf + n + 1; *p = 0;\r
364                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
365 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
366                     }\r
367                 }\r
368             }\r
369             n = i + 1;\r
370         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
371             switch(k) {\r
372               case 'n': k = '\n'; break;\r
373               case 'r': k = '\r'; break;\r
374               case 't': k = '\t'; break;\r
375             }\r
376             languageBuf[--i] = k;\r
377         }\r
378         i++;\r
379     }\r
380     fclose(f);\r
381     barbaric = (j != 0);\r
382     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
383 }\r
384 \r
385 char *\r
386 T_(char *s)\r
387 {   // return the translation of the given string\r
388     // efficiency can be improved a lot...\r
389     int i=0;\r
390 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
391     if(!barbaric) return s;\r
392     if(!s) return ""; // sanity\r
393     while(english[i]) {\r
394         if(!strcmp(s, english[i])) return foreign[i];\r
395         i++;\r
396     }\r
397     return s;\r
398 }\r
399 \r
400 void\r
401 Translate(HWND hDlg, int dialogID)\r
402 {   // translate all text items in the given dialog\r
403     int i=0, j, k;\r
404     char buf[MSG_SIZ], *s;\r
405     if(!barbaric) return;\r
406     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
407     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
408     GetWindowText( hDlg, buf, MSG_SIZ );\r
409     s = T_(buf);\r
410     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
411     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
412         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
413         if(strlen(buf) == 0) continue;\r
414         s = T_(buf);\r
415         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
416     }\r
417 }\r
418 \r
419 HMENU\r
420 TranslateOneMenu(int i, HMENU subMenu)\r
421 {\r
422     int j;\r
423     static MENUITEMINFO info;\r
424 \r
425     info.cbSize = sizeof(MENUITEMINFO);\r
426     info.fMask = MIIM_STATE | MIIM_TYPE;\r
427           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
428             char buf[MSG_SIZ];\r
429             info.dwTypeData = buf;\r
430             info.cch = sizeof(buf);\r
431             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
432             if(i < 10) {
433                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
434                 else menuText[i][j] = strdup(buf); // remember original on first change\r
435             }\r
436             if(buf[0] == NULLCHAR) continue;\r
437             info.dwTypeData = T_(buf);\r
438             info.cch = strlen(buf)+1;\r
439             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
440           }\r
441     return subMenu;\r
442 }\r
443 \r
444 void\r
445 TranslateMenus(int addLanguage)\r
446 {\r
447     int i;\r
448     WIN32_FIND_DATA fileData;\r
449     HANDLE hFind;\r
450 #define IDM_English 1970\r
451     if(1) {\r
452         HMENU mainMenu = GetMenu(hwndMain);\r
453         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
454           HMENU subMenu = GetSubMenu(mainMenu, i);\r
455           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
456                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
457           TranslateOneMenu(i, subMenu);\r
458         }\r
459         DrawMenuBar(hwndMain);\r
460     }\r
461 \r
462     if(!addLanguage) return;\r
463     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
464         HMENU mainMenu = GetMenu(hwndMain);\r
465         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
466         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
467         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
468         i = 0; lastChecked = IDM_English;\r
469         do {\r
470             char *p, *q = fileData.cFileName;\r
471             int checkFlag = MF_UNCHECKED;\r
472             languageFile[i] = strdup(q);\r
473             if(barbaric && !strcmp(oldLanguage, q)) {\r
474                 checkFlag = MF_CHECKED;\r
475                 lastChecked = IDM_English + i + 1;\r
476                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
477             }\r
478             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
479             p = strstr(fileData.cFileName, ".lng");\r
480             if(p) *p = 0;\r
481             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
482         } while(FindNextFile(hFind, &fileData));\r
483         FindClose(hFind);\r
484     }\r
485 }\r
486 \r
487 #endif\r
488 \r
489 typedef struct {\r
490   char *name;\r
491   int squareSize;\r
492   int lineGap;\r
493   int smallLayout;\r
494   int tinyLayout;\r
495   int cliWidth, cliHeight;\r
496 } SizeInfo;\r
497 \r
498 SizeInfo sizeInfo[] = \r
499 {\r
500   { "tiny",     21, 0, 1, 1, 0, 0 },\r
501   { "teeny",    25, 1, 1, 1, 0, 0 },\r
502   { "dinky",    29, 1, 1, 1, 0, 0 },\r
503   { "petite",   33, 1, 1, 1, 0, 0 },\r
504   { "slim",     37, 2, 1, 0, 0, 0 },\r
505   { "small",    40, 2, 1, 0, 0, 0 },\r
506   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
507   { "middling", 49, 2, 0, 0, 0, 0 },\r
508   { "average",  54, 2, 0, 0, 0, 0 },\r
509   { "moderate", 58, 3, 0, 0, 0, 0 },\r
510   { "medium",   64, 3, 0, 0, 0, 0 },\r
511   { "bulky",    72, 3, 0, 0, 0, 0 },\r
512   { "large",    80, 3, 0, 0, 0, 0 },\r
513   { "big",      87, 3, 0, 0, 0, 0 },\r
514   { "huge",     95, 3, 0, 0, 0, 0 },\r
515   { "giant",    108, 3, 0, 0, 0, 0 },\r
516   { "colossal", 116, 4, 0, 0, 0, 0 },\r
517   { "titanic",  129, 4, 0, 0, 0, 0 },\r
518   { NULL, 0, 0, 0, 0, 0, 0 }\r
519 };\r
520 \r
521 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
522 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
523 {\r
524   { 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
525   { 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
526   { 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
527   { 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
528   { 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
529   { 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
530   { 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
531   { 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
532   { 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
533   { 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
534   { 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
535   { 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
536   { 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
537   { 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
538   { 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
539   { 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
540   { 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
541   { 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
542 };\r
543 \r
544 MyFont *font[NUM_SIZES][NUM_FONTS];\r
545 \r
546 typedef struct {\r
547   char *label;\r
548   int id;\r
549   HWND hwnd;\r
550   WNDPROC wndproc;\r
551 } MyButtonDesc;\r
552 \r
553 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
554 #define N_BUTTONS 5\r
555 \r
556 MyButtonDesc buttonDesc[N_BUTTONS] =\r
557 {\r
558   {"<<", IDM_ToStart, NULL, NULL},\r
559   {"<", IDM_Backward, NULL, NULL},\r
560   {"P", IDM_Pause, NULL, NULL},\r
561   {">", IDM_Forward, NULL, NULL},\r
562   {">>", IDM_ToEnd, NULL, NULL},\r
563 };\r
564 \r
565 int tinyLayout = 0, smallLayout = 0;\r
566 #define MENU_BAR_ITEMS 9\r
567 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
568   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
569   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
570 };\r
571 \r
572 \r
573 MySound sounds[(int)NSoundClasses];\r
574 MyTextAttribs textAttribs[(int)NColorClasses];\r
575 \r
576 MyColorizeAttribs colorizeAttribs[] = {\r
577   { (COLORREF)0, 0, N_("Shout Text") },\r
578   { (COLORREF)0, 0, N_("SShout/CShout") },\r
579   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
580   { (COLORREF)0, 0, N_("Channel Text") },\r
581   { (COLORREF)0, 0, N_("Kibitz Text") },\r
582   { (COLORREF)0, 0, N_("Tell Text") },\r
583   { (COLORREF)0, 0, N_("Challenge Text") },\r
584   { (COLORREF)0, 0, N_("Request Text") },\r
585   { (COLORREF)0, 0, N_("Seek Text") },\r
586   { (COLORREF)0, 0, N_("Normal Text") },\r
587   { (COLORREF)0, 0, N_("None") }\r
588 };\r
589 \r
590 \r
591 \r
592 static char *commentTitle;\r
593 static char *commentText;\r
594 static int commentIndex;\r
595 static Boolean editComment = FALSE;\r
596 \r
597 \r
598 char errorTitle[MSG_SIZ];\r
599 char errorMessage[2*MSG_SIZ];\r
600 HWND errorDialog = NULL;\r
601 BOOLEAN moveErrorMessageUp = FALSE;\r
602 BOOLEAN consoleEcho = TRUE;\r
603 CHARFORMAT consoleCF;\r
604 COLORREF consoleBackgroundColor;\r
605 \r
606 char *programVersion;\r
607 \r
608 #define CPReal 1\r
609 #define CPComm 2\r
610 #define CPSock 3\r
611 #define CPRcmd 4\r
612 typedef int CPKind;\r
613 \r
614 typedef struct {\r
615   CPKind kind;\r
616   HANDLE hProcess;\r
617   DWORD pid;\r
618   HANDLE hTo;\r
619   HANDLE hFrom;\r
620   SOCKET sock;\r
621   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
622 } ChildProc;\r
623 \r
624 #define INPUT_SOURCE_BUF_SIZE 4096\r
625 \r
626 typedef struct _InputSource {\r
627   CPKind kind;\r
628   HANDLE hFile;\r
629   SOCKET sock;\r
630   int lineByLine;\r
631   HANDLE hThread;\r
632   DWORD id;\r
633   char buf[INPUT_SOURCE_BUF_SIZE];\r
634   char *next;\r
635   DWORD count;\r
636   int error;\r
637   InputCallback func;\r
638   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
639   VOIDSTAR closure;\r
640 } InputSource;\r
641 \r
642 InputSource *consoleInputSource;\r
643 \r
644 DCB dcb;\r
645 \r
646 /* forward */\r
647 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
648 VOID ConsoleCreate();\r
649 LRESULT CALLBACK\r
650   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
651 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
652 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
653 VOID ParseCommSettings(char *arg, DCB *dcb);\r
654 LRESULT CALLBACK\r
655   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
656 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
657 void ParseIcsTextMenu(char *icsTextMenuString);\r
658 VOID PopUpNameDialog(char firstchar);\r
659 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
660 \r
661 /* [AS] */\r
662 int NewGameFRC();\r
663 int GameListOptions();\r
664 \r
665 int dummy; // [HGM] for obsolete args\r
666 \r
667 HWND hwndMain = NULL;        /* root window*/\r
668 HWND hwndConsole = NULL;\r
669 HWND commentDialog = NULL;\r
670 HWND moveHistoryDialog = NULL;\r
671 HWND evalGraphDialog = NULL;\r
672 HWND engineOutputDialog = NULL;\r
673 HWND gameListDialog = NULL;\r
674 HWND editTagsDialog = NULL;\r
675 \r
676 int commentUp = FALSE;\r
677 \r
678 WindowPlacement wpMain;\r
679 WindowPlacement wpConsole;\r
680 WindowPlacement wpComment;\r
681 WindowPlacement wpMoveHistory;\r
682 WindowPlacement wpEvalGraph;\r
683 WindowPlacement wpEngineOutput;\r
684 WindowPlacement wpGameList;\r
685 WindowPlacement wpTags;\r
686 \r
687 VOID EngineOptionsPopup(); // [HGM] settings\r
688 \r
689 VOID GothicPopUp(char *title, VariantClass variant);\r
690 /*\r
691  * Setting "frozen" should disable all user input other than deleting\r
692  * the window.  We do this while engines are initializing themselves.\r
693  */\r
694 static int frozen = 0;\r
695 static int oldMenuItemState[MENU_BAR_ITEMS];\r
696 void FreezeUI()\r
697 {\r
698   HMENU hmenu;\r
699   int i;\r
700 \r
701   if (frozen) return;\r
702   frozen = 1;\r
703   hmenu = GetMenu(hwndMain);\r
704   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
705     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
706   }\r
707   DrawMenuBar(hwndMain);\r
708 }\r
709 \r
710 /* Undo a FreezeUI */\r
711 void ThawUI()\r
712 {\r
713   HMENU hmenu;\r
714   int i;\r
715 \r
716   if (!frozen) return;\r
717   frozen = 0;\r
718   hmenu = GetMenu(hwndMain);\r
719   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
720     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
721   }\r
722   DrawMenuBar(hwndMain);\r
723 }\r
724 \r
725 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
726 \r
727 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
728 #ifdef JAWS\r
729 #include "jaws.c"\r
730 #else\r
731 #define JAWS_INIT\r
732 #define JAWS_ARGS\r
733 #define JAWS_ALT_INTERCEPT\r
734 #define JAWS_KB_NAVIGATION\r
735 #define JAWS_MENU_ITEMS\r
736 #define JAWS_SILENCE\r
737 #define JAWS_REPLAY\r
738 #define JAWS_ACCEL\r
739 #define JAWS_COPYRIGHT\r
740 #define JAWS_DELETE(X) X\r
741 #define SAYMACHINEMOVE()\r
742 #define SAY(X)\r
743 #endif\r
744 \r
745 /*---------------------------------------------------------------------------*\\r
746  *\r
747  * WinMain\r
748  *\r
749 \*---------------------------------------------------------------------------*/\r
750 \r
751 int APIENTRY\r
752 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
753         LPSTR lpCmdLine, int nCmdShow)\r
754 {\r
755   MSG msg;\r
756   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
757 //  INITCOMMONCONTROLSEX ex;\r
758 \r
759   debugFP = stderr;\r
760 \r
761   LoadLibrary("RICHED32.DLL");\r
762   consoleCF.cbSize = sizeof(CHARFORMAT);\r
763 \r
764   if (!InitApplication(hInstance)) {\r
765     return (FALSE);\r
766   }\r
767   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
768     return (FALSE);\r
769   }\r
770 \r
771   JAWS_INIT\r
772   TranslateMenus(1);\r
773 \r
774 //  InitCommonControlsEx(&ex);\r
775   InitCommonControls();\r
776 \r
777   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
778   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
779   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
780 \r
781   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
782 \r
783   while (GetMessage(&msg, /* message structure */\r
784                     NULL, /* handle of window receiving the message */\r
785                     0,    /* lowest message to examine */\r
786                     0))   /* highest message to examine */\r
787     {\r
788 \r
789       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
790         // [HGM] navigate: switch between all windows with tab\r
791         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
792         int i, currentElement = 0;\r
793 \r
794         // first determine what element of the chain we come from (if any)\r
795         if(appData.icsActive) {\r
796             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
797             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
798         }\r
799         if(engineOutputDialog && EngineOutputIsUp()) {\r
800             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
801             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
802         }\r
803         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
804             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
805         }\r
806         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
807         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
808         if(msg.hwnd == e1)                 currentElement = 2; else\r
809         if(msg.hwnd == e2)                 currentElement = 3; else\r
810         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
811         if(msg.hwnd == mh)                currentElement = 4; else\r
812         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
813         if(msg.hwnd == hText)  currentElement = 5; else\r
814         if(msg.hwnd == hInput) currentElement = 6; else\r
815         for (i = 0; i < N_BUTTONS; i++) {\r
816             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
817         }\r
818 \r
819         // determine where to go to\r
820         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
821           do {\r
822             currentElement = (currentElement + direction) % 7;\r
823             switch(currentElement) {\r
824                 case 0:\r
825                   h = hwndMain; break; // passing this case always makes the loop exit\r
826                 case 1:\r
827                   h = buttonDesc[0].hwnd; break; // could be NULL\r
828                 case 2:\r
829                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
830                   h = e1; break;\r
831                 case 3:\r
832                   if(!EngineOutputIsUp()) continue;\r
833                   h = e2; break;\r
834                 case 4:\r
835                   if(!MoveHistoryIsUp()) continue;\r
836                   h = mh; break;\r
837 //              case 6: // input to eval graph does not seem to get here!\r
838 //                if(!EvalGraphIsUp()) continue;\r
839 //                h = evalGraphDialog; break;\r
840                 case 5:\r
841                   if(!appData.icsActive) continue;\r
842                   SAY("display");\r
843                   h = hText; break;\r
844                 case 6:\r
845                   if(!appData.icsActive) continue;\r
846                   SAY("input");\r
847                   h = hInput; break;\r
848             }\r
849           } while(h == 0);\r
850 \r
851           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
852           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
853           SetFocus(h);\r
854 \r
855           continue; // this message now has been processed\r
856         }\r
857       }\r
858 \r
859       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
860           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
861           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
862           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
863           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
864           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
865           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
866           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
867           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
868           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
869         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
870         for(i=0; i<MAX_CHAT; i++) \r
871             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
872                 done = 1; break;\r
873         }\r
874         if(done) continue; // [HGM] chat: end patch\r
875         TranslateMessage(&msg); /* Translates virtual key codes */\r
876         DispatchMessage(&msg);  /* Dispatches message to window */\r
877       }\r
878     }\r
879 \r
880 \r
881   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
882 }\r
883 \r
884 /*---------------------------------------------------------------------------*\\r
885  *\r
886  * Initialization functions\r
887  *\r
888 \*---------------------------------------------------------------------------*/\r
889 \r
890 void\r
891 SetUserLogo()\r
892 {   // update user logo if necessary\r
893     static char oldUserName[MSG_SIZ], *curName;\r
894 \r
895     if(appData.autoLogo) {\r
896           curName = UserName();\r
897           if(strcmp(curName, oldUserName)) {\r
898             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
899                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
900                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
901                 if(userLogo == NULL)\r
902                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
903           }\r
904     }\r
905 }\r
906 \r
907 BOOL\r
908 InitApplication(HINSTANCE hInstance)\r
909 {\r
910   WNDCLASS wc;\r
911 \r
912   /* Fill in window class structure with parameters that describe the */\r
913   /* main window. */\r
914 \r
915   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
916   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
917   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
918   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
919   wc.hInstance     = hInstance;         /* Owner of this class */\r
920   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
921   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
922   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
923   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
924   wc.lpszClassName = szAppName;                 /* Name to register as */\r
925 \r
926   /* Register the window class and return success/failure code. */\r
927   if (!RegisterClass(&wc)) return FALSE;\r
928 \r
929   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
930   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
931   wc.cbClsExtra    = 0;\r
932   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
933   wc.hInstance     = hInstance;\r
934   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
935   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
936   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
937   wc.lpszMenuName  = NULL;\r
938   wc.lpszClassName = szConsoleName;\r
939 \r
940   if (!RegisterClass(&wc)) return FALSE;\r
941   return TRUE;\r
942 }\r
943 \r
944 \r
945 /* Set by InitInstance, used by EnsureOnScreen */\r
946 int screenHeight, screenWidth;\r
947 \r
948 void\r
949 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
950 {\r
951 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
952   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
953   if (*x > screenWidth - 32) *x = 0;\r
954   if (*y > screenHeight - 32) *y = 0;\r
955   if (*x < minX) *x = minX;\r
956   if (*y < minY) *y = minY;\r
957 }\r
958 \r
959 VOID\r
960 LoadLogo(ChessProgramState *cps, int n)\r
961 {\r
962   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
963       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
964 \r
965       if (cps->programLogo == NULL && appData.debugMode) {\r
966           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
967       }\r
968   } else if(appData.autoLogo) {\r
969       if(appData.firstDirectory && appData.directory[n][0]) {\r
970         char buf[MSG_SIZ];\r
971           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.directory[n]);\r
972         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
973       }\r
974   }\r
975 }\r
976 \r
977 BOOL\r
978 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
979 {\r
980   HWND hwnd; /* Main window handle. */\r
981   int ibs;\r
982   WINDOWPLACEMENT wp;\r
983   char *filepart;\r
984 \r
985   hInst = hInstance;    /* Store instance handle in our global variable */\r
986   programName = szAppName;\r
987 \r
988   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
989     *filepart = NULLCHAR;\r
990   } else {\r
991     GetCurrentDirectory(MSG_SIZ, installDir);\r
992   }\r
993   safeStrCpy(homeDir, installDir, MSG_SIZ);\r
994   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
995   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
996   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
997   /* xboard, and older WinBoards, controlled the move sound with the\r
998      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
999      always turn the option on (so that the backend will call us),\r
1000      then let the user turn the sound off by setting it to silence if\r
1001      desired.  To accommodate old winboard.ini files saved by old\r
1002      versions of WinBoard, we also turn off the sound if the option\r
1003      was initially set to false. [HGM] taken out of InitAppData */\r
1004   if (!appData.ringBellAfterMoves) {\r
1005     sounds[(int)SoundMove].name = strdup("");\r
1006     appData.ringBellAfterMoves = TRUE;\r
1007   }\r
1008   if (appData.debugMode) {\r
1009     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1010     setbuf(debugFP, NULL);\r
1011   }\r
1012 \r
1013   LoadLanguageFile(appData.language);\r
1014 \r
1015   InitBackEnd1();\r
1016 \r
1017 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1018 //  InitEngineUCI( installDir, &second );\r
1019 \r
1020   /* Create a main window for this application instance. */\r
1021   hwnd = CreateWindow(szAppName, szTitle,\r
1022                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1023                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1024                       NULL, NULL, hInstance, NULL);\r
1025   hwndMain = hwnd;\r
1026 \r
1027   /* If window could not be created, return "failure" */\r
1028   if (!hwnd) {\r
1029     return (FALSE);\r
1030   }\r
1031 \r
1032   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1033   LoadLogo(&first, 0);\r
1034 \r
1035   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1036       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1037 \r
1038       if (second.programLogo == NULL && appData.debugMode) {\r
1039           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1040       }\r
1041   } else if(appData.autoLogo) {\r
1042       char buf[MSG_SIZ];\r
1043       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1044         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1045         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1046       } else\r
1047       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1048         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1049         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1050       }\r
1051   }\r
1052 \r
1053   SetUserLogo();\r
1054 \r
1055   iconWhite = LoadIcon(hInstance, "icon_white");\r
1056   iconBlack = LoadIcon(hInstance, "icon_black");\r
1057   iconCurrent = iconWhite;\r
1058   InitDrawingColors();\r
1059   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1060   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1061   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1062     /* Compute window size for each board size, and use the largest\r
1063        size that fits on this screen as the default. */\r
1064     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1065     if (boardSize == (BoardSize)-1 &&\r
1066         winH <= screenHeight\r
1067            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1068         && winW <= screenWidth) {\r
1069       boardSize = (BoardSize)ibs;\r
1070     }\r
1071   }\r
1072 \r
1073   InitDrawingSizes(boardSize, 0);\r
1074   InitMenuChecks();\r
1075   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1076 \r
1077   /* [AS] Load textures if specified */\r
1078   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1079   \r
1080   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1081       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1082       liteBackTextureMode = appData.liteBackTextureMode;\r
1083 \r
1084       if (liteBackTexture == NULL && appData.debugMode) {\r
1085           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1086       }\r
1087   }\r
1088   \r
1089   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1090       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1091       darkBackTextureMode = appData.darkBackTextureMode;\r
1092 \r
1093       if (darkBackTexture == NULL && appData.debugMode) {\r
1094           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1095       }\r
1096   }\r
1097 \r
1098   mysrandom( (unsigned) time(NULL) );\r
1099 \r
1100   /* [AS] Restore layout */\r
1101   if( wpMoveHistory.visible ) {\r
1102       MoveHistoryPopUp();\r
1103   }\r
1104 \r
1105   if( wpEvalGraph.visible ) {\r
1106       EvalGraphPopUp();\r
1107   }\r
1108 \r
1109   if( wpEngineOutput.visible ) {\r
1110       EngineOutputPopUp();\r
1111   }\r
1112 \r
1113   /* Make the window visible; update its client area; and return "success" */\r
1114   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1115   wp.length = sizeof(WINDOWPLACEMENT);\r
1116   wp.flags = 0;\r
1117   wp.showCmd = nCmdShow;\r
1118   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1119   wp.rcNormalPosition.left = wpMain.x;\r
1120   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1121   wp.rcNormalPosition.top = wpMain.y;\r
1122   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1123   SetWindowPlacement(hwndMain, &wp);\r
1124 \r
1125   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1126 \r
1127   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1128                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1129 \r
1130   if (hwndConsole) {\r
1131 #if AOT_CONSOLE\r
1132     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1133                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1134 #endif\r
1135     ShowWindow(hwndConsole, nCmdShow);\r
1136     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1137       char buf[MSG_SIZ], *p = buf, *q;\r
1138         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1139       do {\r
1140         q = strchr(p, ';');\r
1141         if(q) *q++ = 0;\r
1142         if(*p) ChatPopUp(p);\r
1143       } while(p=q);\r
1144     }\r
1145     SetActiveWindow(hwndConsole);\r
1146   }\r
1147   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1148   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1149 \r
1150   return TRUE;\r
1151 \r
1152 }\r
1153 \r
1154 VOID\r
1155 InitMenuChecks()\r
1156 {\r
1157   HMENU hmenu = GetMenu(hwndMain);\r
1158 \r
1159   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1160                         MF_BYCOMMAND|((appData.icsActive &&\r
1161                                        *appData.icsCommPort != NULLCHAR) ?\r
1162                                       MF_ENABLED : MF_GRAYED));\r
1163   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1164                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1165                                      MF_CHECKED : MF_UNCHECKED));\r
1166 }\r
1167 \r
1168 //---------------------------------------------------------------------------------------------------------\r
1169 \r
1170 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1171 #define XBOARD FALSE\r
1172 \r
1173 #define OPTCHAR "/"\r
1174 #define SEPCHAR "="\r
1175 \r
1176 #include "args.h"\r
1177 \r
1178 // front-end part of option handling\r
1179 \r
1180 VOID\r
1181 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1182 {\r
1183   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1184   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1185   DeleteDC(hdc);\r
1186   lf->lfWidth = 0;\r
1187   lf->lfEscapement = 0;\r
1188   lf->lfOrientation = 0;\r
1189   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1190   lf->lfItalic = mfp->italic;\r
1191   lf->lfUnderline = mfp->underline;\r
1192   lf->lfStrikeOut = mfp->strikeout;\r
1193   lf->lfCharSet = mfp->charset;\r
1194   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1195   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1196   lf->lfQuality = DEFAULT_QUALITY;\r
1197   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1198     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1199 }\r
1200 \r
1201 void\r
1202 CreateFontInMF(MyFont *mf)\r
1203\r
1204   LFfromMFP(&mf->lf, &mf->mfp);\r
1205   if (mf->hf) DeleteObject(mf->hf);\r
1206   mf->hf = CreateFontIndirect(&mf->lf);\r
1207 }\r
1208 \r
1209 // [HGM] This platform-dependent table provides the location for storing the color info\r
1210 void *\r
1211 colorVariable[] = {\r
1212   &whitePieceColor, \r
1213   &blackPieceColor, \r
1214   &lightSquareColor,\r
1215   &darkSquareColor, \r
1216   &highlightSquareColor,\r
1217   &premoveHighlightColor,\r
1218   NULL,\r
1219   &consoleBackgroundColor,\r
1220   &appData.fontForeColorWhite,\r
1221   &appData.fontBackColorWhite,\r
1222   &appData.fontForeColorBlack,\r
1223   &appData.fontBackColorBlack,\r
1224   &appData.evalHistColorWhite,\r
1225   &appData.evalHistColorBlack,\r
1226   &appData.highlightArrowColor,\r
1227 };\r
1228 \r
1229 /* Command line font name parser.  NULL name means do nothing.\r
1230    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1231    For backward compatibility, syntax without the colon is also\r
1232    accepted, but font names with digits in them won't work in that case.\r
1233 */\r
1234 VOID\r
1235 ParseFontName(char *name, MyFontParams *mfp)\r
1236 {\r
1237   char *p, *q;\r
1238   if (name == NULL) return;\r
1239   p = name;\r
1240   q = strchr(p, ':');\r
1241   if (q) {\r
1242     if (q - p >= sizeof(mfp->faceName))\r
1243       ExitArgError(_("Font name too long:"), name);\r
1244     memcpy(mfp->faceName, p, q - p);\r
1245     mfp->faceName[q - p] = NULLCHAR;\r
1246     p = q + 1;\r
1247   } else {\r
1248     q = mfp->faceName;\r
1249     while (*p && !isdigit(*p)) {\r
1250       *q++ = *p++;\r
1251       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1252         ExitArgError(_("Font name too long:"), name);\r
1253     }\r
1254     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1255     *q = NULLCHAR;\r
1256   }\r
1257   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1258   mfp->pointSize = (float) atof(p);\r
1259   mfp->bold = (strchr(p, 'b') != NULL);\r
1260   mfp->italic = (strchr(p, 'i') != NULL);\r
1261   mfp->underline = (strchr(p, 'u') != NULL);\r
1262   mfp->strikeout = (strchr(p, 's') != NULL);\r
1263   mfp->charset = DEFAULT_CHARSET;\r
1264   q = strchr(p, 'c');\r
1265   if (q)\r
1266     mfp->charset = (BYTE) atoi(q+1);\r
1267 }\r
1268 \r
1269 void\r
1270 ParseFont(char *name, int number)\r
1271 { // wrapper to shield back-end from 'font'\r
1272   ParseFontName(name, &font[boardSize][number]->mfp);\r
1273 }\r
1274 \r
1275 void\r
1276 SetFontDefaults()\r
1277 { // in WB  we have a 2D array of fonts; this initializes their description\r
1278   int i, j;\r
1279   /* Point font array elements to structures and\r
1280      parse default font names */\r
1281   for (i=0; i<NUM_FONTS; i++) {\r
1282     for (j=0; j<NUM_SIZES; j++) {\r
1283       font[j][i] = &fontRec[j][i];\r
1284       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1285     }\r
1286   }\r
1287 }\r
1288 \r
1289 void\r
1290 CreateFonts()\r
1291 { // here we create the actual fonts from the selected descriptions\r
1292   int i, j;\r
1293   for (i=0; i<NUM_FONTS; i++) {\r
1294     for (j=0; j<NUM_SIZES; j++) {\r
1295       CreateFontInMF(font[j][i]);\r
1296     }\r
1297   }\r
1298 }\r
1299 /* Color name parser.\r
1300    X version accepts X color names, but this one\r
1301    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1302 COLORREF\r
1303 ParseColorName(char *name)\r
1304 {\r
1305   int red, green, blue, count;\r
1306   char buf[MSG_SIZ];\r
1307 \r
1308   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1309   if (count != 3) {\r
1310     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1311       &red, &green, &blue);\r
1312   }\r
1313   if (count != 3) {\r
1314     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1315     DisplayError(buf, 0);\r
1316     return RGB(0, 0, 0);\r
1317   }\r
1318   return PALETTERGB(red, green, blue);\r
1319 }\r
1320 \r
1321 void\r
1322 ParseColor(int n, char *name)\r
1323 { // for WinBoard the color is an int, which needs to be derived from the string\r
1324   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1325 }\r
1326 \r
1327 void\r
1328 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1329 {\r
1330   char *e = argValue;\r
1331   int eff = 0;\r
1332 \r
1333   while (*e) {\r
1334     if (*e == 'b')      eff |= CFE_BOLD;\r
1335     else if (*e == 'i') eff |= CFE_ITALIC;\r
1336     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1337     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1338     else if (*e == '#' || isdigit(*e)) break;\r
1339     e++;\r
1340   }\r
1341   *effects = eff;\r
1342   *color   = ParseColorName(e);\r
1343 }\r
1344 \r
1345 void\r
1346 ParseTextAttribs(ColorClass cc, char *s)\r
1347 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1348     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1349     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1350 }\r
1351 \r
1352 void\r
1353 ParseBoardSize(void *addr, char *name)\r
1354 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1355   BoardSize bs = SizeTiny;\r
1356   while (sizeInfo[bs].name != NULL) {\r
1357     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1358         *(BoardSize *)addr = bs;\r
1359         return;\r
1360     }\r
1361     bs++;\r
1362   }\r
1363   ExitArgError(_("Unrecognized board size value"), name);\r
1364 }\r
1365 \r
1366 void\r
1367 LoadAllSounds()\r
1368 { // [HGM] import name from appData first\r
1369   ColorClass cc;\r
1370   SoundClass sc;\r
1371   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1372     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1373     textAttribs[cc].sound.data = NULL;\r
1374     MyLoadSound(&textAttribs[cc].sound);\r
1375   }\r
1376   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1377     textAttribs[cc].sound.name = strdup("");\r
1378     textAttribs[cc].sound.data = NULL;\r
1379   }\r
1380   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1381     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1382     sounds[sc].data = NULL;\r
1383     MyLoadSound(&sounds[sc]);\r
1384   }\r
1385 }\r
1386 \r
1387 void\r
1388 SetCommPortDefaults()\r
1389 {\r
1390    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1391   dcb.DCBlength = sizeof(DCB);\r
1392   dcb.BaudRate = 9600;\r
1393   dcb.fBinary = TRUE;\r
1394   dcb.fParity = FALSE;\r
1395   dcb.fOutxCtsFlow = FALSE;\r
1396   dcb.fOutxDsrFlow = FALSE;\r
1397   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1398   dcb.fDsrSensitivity = FALSE;\r
1399   dcb.fTXContinueOnXoff = TRUE;\r
1400   dcb.fOutX = FALSE;\r
1401   dcb.fInX = FALSE;\r
1402   dcb.fNull = FALSE;\r
1403   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1404   dcb.fAbortOnError = FALSE;\r
1405   dcb.ByteSize = 7;\r
1406   dcb.Parity = SPACEPARITY;\r
1407   dcb.StopBits = ONESTOPBIT;\r
1408 }\r
1409 \r
1410 // [HGM] args: these three cases taken out to stay in front-end\r
1411 void\r
1412 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1413 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1414         // while the curent board size determines the element. This system should be ported to XBoard.\r
1415         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1416         int bs;\r
1417         for (bs=0; bs<NUM_SIZES; bs++) {\r
1418           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1419           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1420           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1421             ad->argName, mfp->faceName, mfp->pointSize,\r
1422             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1423             mfp->bold ? "b" : "",\r
1424             mfp->italic ? "i" : "",\r
1425             mfp->underline ? "u" : "",\r
1426             mfp->strikeout ? "s" : "",\r
1427             (int)mfp->charset);\r
1428         }\r
1429       }\r
1430 \r
1431 void\r
1432 ExportSounds()\r
1433 { // [HGM] copy the names from the internal WB variables to appData\r
1434   ColorClass cc;\r
1435   SoundClass sc;\r
1436   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1437     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1438   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1439     (&appData.soundMove)[sc] = sounds[sc].name;\r
1440 }\r
1441 \r
1442 void\r
1443 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1444 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1445         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1446         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1447           (ta->effects & CFE_BOLD) ? "b" : "",\r
1448           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1449           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1450           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1451           (ta->effects) ? " " : "",\r
1452           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1453       }\r
1454 \r
1455 void\r
1456 SaveColor(FILE *f, ArgDescriptor *ad)\r
1457 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1458         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1459         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1460           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1461 }\r
1462 \r
1463 void\r
1464 SaveBoardSize(FILE *f, char *name, void *addr)\r
1465 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1466   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1467 }\r
1468 \r
1469 void\r
1470 ParseCommPortSettings(char *s)\r
1471 { // wrapper to keep dcb from back-end\r
1472   ParseCommSettings(s, &dcb);\r
1473 }\r
1474 \r
1475 void\r
1476 GetWindowCoords()\r
1477 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1478   GetActualPlacement(hwndMain, &wpMain);\r
1479   GetActualPlacement(hwndConsole, &wpConsole);\r
1480   GetActualPlacement(commentDialog, &wpComment);\r
1481   GetActualPlacement(editTagsDialog, &wpTags);\r
1482   GetActualPlacement(gameListDialog, &wpGameList);\r
1483   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1484   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1485   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1486 }\r
1487 \r
1488 void\r
1489 PrintCommPortSettings(FILE *f, char *name)\r
1490 { // wrapper to shield back-end from DCB\r
1491       PrintCommSettings(f, name, &dcb);\r
1492 }\r
1493 \r
1494 int\r
1495 MySearchPath(char *installDir, char *name, char *fullname)\r
1496 {\r
1497   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1498   if(name[0]== '%') {\r
1499     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1500     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1501       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1502       *strchr(buf, '%') = 0;\r
1503       strcat(fullname, getenv(buf));\r
1504       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1505     }\r
1506     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1507     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1508     return (int) strlen(fullname);\r
1509   }\r
1510   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1511 }\r
1512 \r
1513 int\r
1514 MyGetFullPathName(char *name, char *fullname)\r
1515 {\r
1516   char *dummy;\r
1517   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1518 }\r
1519 \r
1520 int\r
1521 MainWindowUp()\r
1522 { // [HGM] args: allows testing if main window is realized from back-end\r
1523   return hwndMain != NULL;\r
1524 }\r
1525 \r
1526 void\r
1527 PopUpStartupDialog()\r
1528 {\r
1529     FARPROC lpProc;\r
1530     \r
1531     LoadLanguageFile(appData.language);\r
1532     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1533     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1534     FreeProcInstance(lpProc);\r
1535 }\r
1536 \r
1537 /*---------------------------------------------------------------------------*\\r
1538  *\r
1539  * GDI board drawing routines\r
1540  *\r
1541 \*---------------------------------------------------------------------------*/\r
1542 \r
1543 /* [AS] Draw square using background texture */\r
1544 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1545 {\r
1546     XFORM   x;\r
1547 \r
1548     if( mode == 0 ) {\r
1549         return; /* Should never happen! */\r
1550     }\r
1551 \r
1552     SetGraphicsMode( dst, GM_ADVANCED );\r
1553 \r
1554     switch( mode ) {\r
1555     case 1:\r
1556         /* Identity */\r
1557         break;\r
1558     case 2:\r
1559         /* X reflection */\r
1560         x.eM11 = -1.0;\r
1561         x.eM12 = 0;\r
1562         x.eM21 = 0;\r
1563         x.eM22 = 1.0;\r
1564         x.eDx = (FLOAT) dw + dx - 1;\r
1565         x.eDy = 0;\r
1566         dx = 0;\r
1567         SetWorldTransform( dst, &x );\r
1568         break;\r
1569     case 3:\r
1570         /* Y reflection */\r
1571         x.eM11 = 1.0;\r
1572         x.eM12 = 0;\r
1573         x.eM21 = 0;\r
1574         x.eM22 = -1.0;\r
1575         x.eDx = 0;\r
1576         x.eDy = (FLOAT) dh + dy - 1;\r
1577         dy = 0;\r
1578         SetWorldTransform( dst, &x );\r
1579         break;\r
1580     case 4:\r
1581         /* X/Y flip */\r
1582         x.eM11 = 0;\r
1583         x.eM12 = 1.0;\r
1584         x.eM21 = 1.0;\r
1585         x.eM22 = 0;\r
1586         x.eDx = (FLOAT) dx;\r
1587         x.eDy = (FLOAT) dy;\r
1588         dx = 0;\r
1589         dy = 0;\r
1590         SetWorldTransform( dst, &x );\r
1591         break;\r
1592     }\r
1593 \r
1594     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1595 \r
1596     x.eM11 = 1.0;\r
1597     x.eM12 = 0;\r
1598     x.eM21 = 0;\r
1599     x.eM22 = 1.0;\r
1600     x.eDx = 0;\r
1601     x.eDy = 0;\r
1602     SetWorldTransform( dst, &x );\r
1603 \r
1604     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1605 }\r
1606 \r
1607 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1608 enum {\r
1609     PM_WP = (int) WhitePawn, \r
1610     PM_WN = (int) WhiteKnight, \r
1611     PM_WB = (int) WhiteBishop, \r
1612     PM_WR = (int) WhiteRook, \r
1613     PM_WQ = (int) WhiteQueen, \r
1614     PM_WF = (int) WhiteFerz, \r
1615     PM_WW = (int) WhiteWazir, \r
1616     PM_WE = (int) WhiteAlfil, \r
1617     PM_WM = (int) WhiteMan, \r
1618     PM_WO = (int) WhiteCannon, \r
1619     PM_WU = (int) WhiteUnicorn, \r
1620     PM_WH = (int) WhiteNightrider, \r
1621     PM_WA = (int) WhiteAngel, \r
1622     PM_WC = (int) WhiteMarshall, \r
1623     PM_WAB = (int) WhiteCardinal, \r
1624     PM_WD = (int) WhiteDragon, \r
1625     PM_WL = (int) WhiteLance, \r
1626     PM_WS = (int) WhiteCobra, \r
1627     PM_WV = (int) WhiteFalcon, \r
1628     PM_WSG = (int) WhiteSilver, \r
1629     PM_WG = (int) WhiteGrasshopper, \r
1630     PM_WK = (int) WhiteKing,\r
1631     PM_BP = (int) BlackPawn, \r
1632     PM_BN = (int) BlackKnight, \r
1633     PM_BB = (int) BlackBishop, \r
1634     PM_BR = (int) BlackRook, \r
1635     PM_BQ = (int) BlackQueen, \r
1636     PM_BF = (int) BlackFerz, \r
1637     PM_BW = (int) BlackWazir, \r
1638     PM_BE = (int) BlackAlfil, \r
1639     PM_BM = (int) BlackMan,\r
1640     PM_BO = (int) BlackCannon, \r
1641     PM_BU = (int) BlackUnicorn, \r
1642     PM_BH = (int) BlackNightrider, \r
1643     PM_BA = (int) BlackAngel, \r
1644     PM_BC = (int) BlackMarshall, \r
1645     PM_BG = (int) BlackGrasshopper, \r
1646     PM_BAB = (int) BlackCardinal,\r
1647     PM_BD = (int) BlackDragon,\r
1648     PM_BL = (int) BlackLance,\r
1649     PM_BS = (int) BlackCobra,\r
1650     PM_BV = (int) BlackFalcon,\r
1651     PM_BSG = (int) BlackSilver,\r
1652     PM_BK = (int) BlackKing\r
1653 };\r
1654 \r
1655 static HFONT hPieceFont = NULL;\r
1656 static HBITMAP hPieceMask[(int) EmptySquare];\r
1657 static HBITMAP hPieceFace[(int) EmptySquare];\r
1658 static int fontBitmapSquareSize = 0;\r
1659 static char pieceToFontChar[(int) EmptySquare] =\r
1660                               { 'p', 'n', 'b', 'r', 'q', \r
1661                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1662                       'k', 'o', 'm', 'v', 't', 'w', \r
1663                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1664                                                               'l' };\r
1665 \r
1666 extern BOOL SetCharTable( char *table, const char * map );\r
1667 /* [HGM] moved to backend.c */\r
1668 \r
1669 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1670 {\r
1671     HBRUSH hbrush;\r
1672     BYTE r1 = GetRValue( color );\r
1673     BYTE g1 = GetGValue( color );\r
1674     BYTE b1 = GetBValue( color );\r
1675     BYTE r2 = r1 / 2;\r
1676     BYTE g2 = g1 / 2;\r
1677     BYTE b2 = b1 / 2;\r
1678     RECT rc;\r
1679 \r
1680     /* Create a uniform background first */\r
1681     hbrush = CreateSolidBrush( color );\r
1682     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1683     FillRect( hdc, &rc, hbrush );\r
1684     DeleteObject( hbrush );\r
1685     \r
1686     if( mode == 1 ) {\r
1687         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1688         int steps = squareSize / 2;\r
1689         int i;\r
1690 \r
1691         for( i=0; i<steps; i++ ) {\r
1692             BYTE r = r1 - (r1-r2) * i / steps;\r
1693             BYTE g = g1 - (g1-g2) * i / steps;\r
1694             BYTE b = b1 - (b1-b2) * i / steps;\r
1695 \r
1696             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1697             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1698             FillRect( hdc, &rc, hbrush );\r
1699             DeleteObject(hbrush);\r
1700         }\r
1701     }\r
1702     else if( mode == 2 ) {\r
1703         /* Diagonal gradient, good more or less for every piece */\r
1704         POINT triangle[3];\r
1705         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1706         HBRUSH hbrush_old;\r
1707         int steps = squareSize;\r
1708         int i;\r
1709 \r
1710         triangle[0].x = squareSize - steps;\r
1711         triangle[0].y = squareSize;\r
1712         triangle[1].x = squareSize;\r
1713         triangle[1].y = squareSize;\r
1714         triangle[2].x = squareSize;\r
1715         triangle[2].y = squareSize - steps;\r
1716 \r
1717         for( i=0; i<steps; i++ ) {\r
1718             BYTE r = r1 - (r1-r2) * i / steps;\r
1719             BYTE g = g1 - (g1-g2) * i / steps;\r
1720             BYTE b = b1 - (b1-b2) * i / steps;\r
1721 \r
1722             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1723             hbrush_old = SelectObject( hdc, hbrush );\r
1724             Polygon( hdc, triangle, 3 );\r
1725             SelectObject( hdc, hbrush_old );\r
1726             DeleteObject(hbrush);\r
1727             triangle[0].x++;\r
1728             triangle[2].y++;\r
1729         }\r
1730 \r
1731         SelectObject( hdc, hpen );\r
1732     }\r
1733 }\r
1734 \r
1735 /*\r
1736     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1737     seems to work ok. The main problem here is to find the "inside" of a chess\r
1738     piece: follow the steps as explained below.\r
1739 */\r
1740 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1741 {\r
1742     HBITMAP hbm;\r
1743     HBITMAP hbm_old;\r
1744     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1745     RECT rc;\r
1746     SIZE sz;\r
1747     POINT pt;\r
1748     int backColor = whitePieceColor; \r
1749     int foreColor = blackPieceColor;\r
1750     \r
1751     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1752         backColor = appData.fontBackColorWhite;\r
1753         foreColor = appData.fontForeColorWhite;\r
1754     }\r
1755     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1756         backColor = appData.fontBackColorBlack;\r
1757         foreColor = appData.fontForeColorBlack;\r
1758     }\r
1759 \r
1760     /* Mask */\r
1761     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1762 \r
1763     hbm_old = SelectObject( hdc, hbm );\r
1764 \r
1765     rc.left = 0;\r
1766     rc.top = 0;\r
1767     rc.right = squareSize;\r
1768     rc.bottom = squareSize;\r
1769 \r
1770     /* Step 1: background is now black */\r
1771     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1772 \r
1773     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1774 \r
1775     pt.x = (squareSize - sz.cx) / 2;\r
1776     pt.y = (squareSize - sz.cy) / 2;\r
1777 \r
1778     SetBkMode( hdc, TRANSPARENT );\r
1779     SetTextColor( hdc, chroma );\r
1780     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1781     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1782 \r
1783     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1784     /* Step 3: the area outside the piece is filled with white */\r
1785 //    FloodFill( hdc, 0, 0, chroma );\r
1786     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1787     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1788     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1789     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1790     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1791     /* \r
1792         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1793         but if the start point is not inside the piece we're lost!\r
1794         There should be a better way to do this... if we could create a region or path\r
1795         from the fill operation we would be fine for example.\r
1796     */\r
1797 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1798     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1799 \r
1800     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1801         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1802         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1803 \r
1804         SelectObject( dc2, bm2 );\r
1805         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1806         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1807         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1808         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1809         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1810 \r
1811         DeleteDC( dc2 );\r
1812         DeleteObject( bm2 );\r
1813     }\r
1814 \r
1815     SetTextColor( hdc, 0 );\r
1816     /* \r
1817         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1818         draw the piece again in black for safety.\r
1819     */\r
1820     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1821 \r
1822     SelectObject( hdc, hbm_old );\r
1823 \r
1824     if( hPieceMask[index] != NULL ) {\r
1825         DeleteObject( hPieceMask[index] );\r
1826     }\r
1827 \r
1828     hPieceMask[index] = hbm;\r
1829 \r
1830     /* Face */\r
1831     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1832 \r
1833     SelectObject( hdc, hbm );\r
1834 \r
1835     {\r
1836         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1837         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1838         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1839 \r
1840         SelectObject( dc1, hPieceMask[index] );\r
1841         SelectObject( dc2, bm2 );\r
1842         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1843         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1844         \r
1845         /* \r
1846             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1847             the piece background and deletes (makes transparent) the rest.\r
1848             Thanks to that mask, we are free to paint the background with the greates\r
1849             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1850             We use this, to make gradients and give the pieces a "roundish" look.\r
1851         */\r
1852         SetPieceBackground( hdc, backColor, 2 );\r
1853         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1854 \r
1855         DeleteDC( dc2 );\r
1856         DeleteDC( dc1 );\r
1857         DeleteObject( bm2 );\r
1858     }\r
1859 \r
1860     SetTextColor( hdc, foreColor );\r
1861     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1862 \r
1863     SelectObject( hdc, hbm_old );\r
1864 \r
1865     if( hPieceFace[index] != NULL ) {\r
1866         DeleteObject( hPieceFace[index] );\r
1867     }\r
1868 \r
1869     hPieceFace[index] = hbm;\r
1870 }\r
1871 \r
1872 static int TranslatePieceToFontPiece( int piece )\r
1873 {\r
1874     switch( piece ) {\r
1875     case BlackPawn:\r
1876         return PM_BP;\r
1877     case BlackKnight:\r
1878         return PM_BN;\r
1879     case BlackBishop:\r
1880         return PM_BB;\r
1881     case BlackRook:\r
1882         return PM_BR;\r
1883     case BlackQueen:\r
1884         return PM_BQ;\r
1885     case BlackKing:\r
1886         return PM_BK;\r
1887     case WhitePawn:\r
1888         return PM_WP;\r
1889     case WhiteKnight:\r
1890         return PM_WN;\r
1891     case WhiteBishop:\r
1892         return PM_WB;\r
1893     case WhiteRook:\r
1894         return PM_WR;\r
1895     case WhiteQueen:\r
1896         return PM_WQ;\r
1897     case WhiteKing:\r
1898         return PM_WK;\r
1899 \r
1900     case BlackAngel:\r
1901         return PM_BA;\r
1902     case BlackMarshall:\r
1903         return PM_BC;\r
1904     case BlackFerz:\r
1905         return PM_BF;\r
1906     case BlackNightrider:\r
1907         return PM_BH;\r
1908     case BlackAlfil:\r
1909         return PM_BE;\r
1910     case BlackWazir:\r
1911         return PM_BW;\r
1912     case BlackUnicorn:\r
1913         return PM_BU;\r
1914     case BlackCannon:\r
1915         return PM_BO;\r
1916     case BlackGrasshopper:\r
1917         return PM_BG;\r
1918     case BlackMan:\r
1919         return PM_BM;\r
1920     case BlackSilver:\r
1921         return PM_BSG;\r
1922     case BlackLance:\r
1923         return PM_BL;\r
1924     case BlackFalcon:\r
1925         return PM_BV;\r
1926     case BlackCobra:\r
1927         return PM_BS;\r
1928     case BlackCardinal:\r
1929         return PM_BAB;\r
1930     case BlackDragon:\r
1931         return PM_BD;\r
1932 \r
1933     case WhiteAngel:\r
1934         return PM_WA;\r
1935     case WhiteMarshall:\r
1936         return PM_WC;\r
1937     case WhiteFerz:\r
1938         return PM_WF;\r
1939     case WhiteNightrider:\r
1940         return PM_WH;\r
1941     case WhiteAlfil:\r
1942         return PM_WE;\r
1943     case WhiteWazir:\r
1944         return PM_WW;\r
1945     case WhiteUnicorn:\r
1946         return PM_WU;\r
1947     case WhiteCannon:\r
1948         return PM_WO;\r
1949     case WhiteGrasshopper:\r
1950         return PM_WG;\r
1951     case WhiteMan:\r
1952         return PM_WM;\r
1953     case WhiteSilver:\r
1954         return PM_WSG;\r
1955     case WhiteLance:\r
1956         return PM_WL;\r
1957     case WhiteFalcon:\r
1958         return PM_WV;\r
1959     case WhiteCobra:\r
1960         return PM_WS;\r
1961     case WhiteCardinal:\r
1962         return PM_WAB;\r
1963     case WhiteDragon:\r
1964         return PM_WD;\r
1965     }\r
1966 \r
1967     return 0;\r
1968 }\r
1969 \r
1970 void CreatePiecesFromFont()\r
1971 {\r
1972     LOGFONT lf;\r
1973     HDC hdc_window = NULL;\r
1974     HDC hdc = NULL;\r
1975     HFONT hfont_old;\r
1976     int fontHeight;\r
1977     int i;\r
1978 \r
1979     if( fontBitmapSquareSize < 0 ) {\r
1980         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1981         return;\r
1982     }\r
1983 \r
1984     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1985         fontBitmapSquareSize = -1;\r
1986         return;\r
1987     }\r
1988 \r
1989     if( fontBitmapSquareSize != squareSize ) {\r
1990         hdc_window = GetDC( hwndMain );\r
1991         hdc = CreateCompatibleDC( hdc_window );\r
1992 \r
1993         if( hPieceFont != NULL ) {\r
1994             DeleteObject( hPieceFont );\r
1995         }\r
1996         else {\r
1997             for( i=0; i<=(int)BlackKing; i++ ) {\r
1998                 hPieceMask[i] = NULL;\r
1999                 hPieceFace[i] = NULL;\r
2000             }\r
2001         }\r
2002 \r
2003         fontHeight = 75;\r
2004 \r
2005         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2006             fontHeight = appData.fontPieceSize;\r
2007         }\r
2008 \r
2009         fontHeight = (fontHeight * squareSize) / 100;\r
2010 \r
2011         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2012         lf.lfWidth = 0;\r
2013         lf.lfEscapement = 0;\r
2014         lf.lfOrientation = 0;\r
2015         lf.lfWeight = FW_NORMAL;\r
2016         lf.lfItalic = 0;\r
2017         lf.lfUnderline = 0;\r
2018         lf.lfStrikeOut = 0;\r
2019         lf.lfCharSet = DEFAULT_CHARSET;\r
2020         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2021         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2022         lf.lfQuality = PROOF_QUALITY;\r
2023         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2024         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2025         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2026 \r
2027         hPieceFont = CreateFontIndirect( &lf );\r
2028 \r
2029         if( hPieceFont == NULL ) {\r
2030             fontBitmapSquareSize = -2;\r
2031         }\r
2032         else {\r
2033             /* Setup font-to-piece character table */\r
2034             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2035                 /* No (or wrong) global settings, try to detect the font */\r
2036                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2037                     /* Alpha */\r
2038                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2039                 }\r
2040                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2041                     /* DiagramTT* family */\r
2042                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2043                 }\r
2044                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2045                     /* Fairy symbols */\r
2046                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2047                 }\r
2048                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2049                     /* Good Companion (Some characters get warped as literal :-( */\r
2050                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2051                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2052                     SetCharTable(pieceToFontChar, s);\r
2053                 }\r
2054                 else {\r
2055                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2056                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2057                 }\r
2058             }\r
2059 \r
2060             /* Create bitmaps */\r
2061             hfont_old = SelectObject( hdc, hPieceFont );\r
2062             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2063                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2064                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2065 \r
2066             SelectObject( hdc, hfont_old );\r
2067 \r
2068             fontBitmapSquareSize = squareSize;\r
2069         }\r
2070     }\r
2071 \r
2072     if( hdc != NULL ) {\r
2073         DeleteDC( hdc );\r
2074     }\r
2075 \r
2076     if( hdc_window != NULL ) {\r
2077         ReleaseDC( hwndMain, hdc_window );\r
2078     }\r
2079 }\r
2080 \r
2081 HBITMAP\r
2082 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2083 {\r
2084   char name[128];\r
2085 \r
2086     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2087   if (gameInfo.event &&\r
2088       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2089       strcmp(name, "k80s") == 0) {\r
2090     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2091   }\r
2092   return LoadBitmap(hinst, name);\r
2093 }\r
2094 \r
2095 \r
2096 /* Insert a color into the program's logical palette\r
2097    structure.  This code assumes the given color is\r
2098    the result of the RGB or PALETTERGB macro, and it\r
2099    knows how those macros work (which is documented).\r
2100 */\r
2101 VOID\r
2102 InsertInPalette(COLORREF color)\r
2103 {\r
2104   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2105 \r
2106   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2107     DisplayFatalError(_("Too many colors"), 0, 1);\r
2108     pLogPal->palNumEntries--;\r
2109     return;\r
2110   }\r
2111 \r
2112   pe->peFlags = (char) 0;\r
2113   pe->peRed = (char) (0xFF & color);\r
2114   pe->peGreen = (char) (0xFF & (color >> 8));\r
2115   pe->peBlue = (char) (0xFF & (color >> 16));\r
2116   return;\r
2117 }\r
2118 \r
2119 \r
2120 VOID\r
2121 InitDrawingColors()\r
2122 {\r
2123   if (pLogPal == NULL) {\r
2124     /* Allocate enough memory for a logical palette with\r
2125      * PALETTESIZE entries and set the size and version fields\r
2126      * of the logical palette structure.\r
2127      */\r
2128     pLogPal = (NPLOGPALETTE)\r
2129       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2130                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2131     pLogPal->palVersion    = 0x300;\r
2132   }\r
2133   pLogPal->palNumEntries = 0;\r
2134 \r
2135   InsertInPalette(lightSquareColor);\r
2136   InsertInPalette(darkSquareColor);\r
2137   InsertInPalette(whitePieceColor);\r
2138   InsertInPalette(blackPieceColor);\r
2139   InsertInPalette(highlightSquareColor);\r
2140   InsertInPalette(premoveHighlightColor);\r
2141 \r
2142   /*  create a logical color palette according the information\r
2143    *  in the LOGPALETTE structure.\r
2144    */\r
2145   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2146 \r
2147   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2148   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2149   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2150   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2151   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2152   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2153   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2154   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2155   /* [AS] Force rendering of the font-based pieces */\r
2156   if( fontBitmapSquareSize > 0 ) {\r
2157     fontBitmapSquareSize = 0;\r
2158   }\r
2159 }\r
2160 \r
2161 \r
2162 int\r
2163 BoardWidth(int boardSize, int n)\r
2164 { /* [HGM] argument n added to allow different width and height */\r
2165   int lineGap = sizeInfo[boardSize].lineGap;\r
2166 \r
2167   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2168       lineGap = appData.overrideLineGap;\r
2169   }\r
2170 \r
2171   return (n + 1) * lineGap +\r
2172           n * sizeInfo[boardSize].squareSize;\r
2173 }\r
2174 \r
2175 /* Respond to board resize by dragging edge */\r
2176 VOID\r
2177 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2178 {\r
2179   BoardSize newSize = NUM_SIZES - 1;\r
2180   static int recurse = 0;\r
2181   if (IsIconic(hwndMain)) return;\r
2182   if (recurse > 0) return;\r
2183   recurse++;\r
2184   while (newSize > 0) {\r
2185         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2186         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2187            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2188     newSize--;\r
2189   } \r
2190   boardSize = newSize;\r
2191   InitDrawingSizes(boardSize, flags);\r
2192   recurse--;\r
2193 }\r
2194 \r
2195 \r
2196 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2197 \r
2198 VOID\r
2199 InitDrawingSizes(BoardSize boardSize, int flags)\r
2200 {\r
2201   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2202   ChessSquare piece;\r
2203   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2204   HDC hdc;\r
2205   SIZE clockSize, messageSize;\r
2206   HFONT oldFont;\r
2207   char buf[MSG_SIZ];\r
2208   char *str;\r
2209   HMENU hmenu = GetMenu(hwndMain);\r
2210   RECT crect, wrect, oldRect;\r
2211   int offby;\r
2212   LOGBRUSH logbrush;\r
2213 \r
2214   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2215   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2216 \r
2217   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2218   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2219 \r
2220   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2221   oldRect.top = wpMain.y;\r
2222   oldRect.right = wpMain.x + wpMain.width;\r
2223   oldRect.bottom = wpMain.y + wpMain.height;\r
2224 \r
2225   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2226   smallLayout = sizeInfo[boardSize].smallLayout;\r
2227   squareSize = sizeInfo[boardSize].squareSize;\r
2228   lineGap = sizeInfo[boardSize].lineGap;\r
2229   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2230 \r
2231   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2232       lineGap = appData.overrideLineGap;\r
2233   }\r
2234 \r
2235   if (tinyLayout != oldTinyLayout) {\r
2236     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2237     if (tinyLayout) {\r
2238       style &= ~WS_SYSMENU;\r
2239       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2240                  "&Minimize\tCtrl+F4");\r
2241     } else {\r
2242       style |= WS_SYSMENU;\r
2243       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2244     }\r
2245     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2246 \r
2247     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2248       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2249         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2250     }\r
2251     DrawMenuBar(hwndMain);\r
2252   }\r
2253 \r
2254   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2255   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2256 \r
2257   /* Get text area sizes */\r
2258   hdc = GetDC(hwndMain);\r
2259   if (appData.clockMode) {\r
2260     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2261   } else {\r
2262     snprintf(buf, MSG_SIZ, _("White"));\r
2263   }\r
2264   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2265   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2266   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2267   str = _("We only care about the height here");\r
2268   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2269   SelectObject(hdc, oldFont);\r
2270   ReleaseDC(hwndMain, hdc);\r
2271 \r
2272   /* Compute where everything goes */\r
2273   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2274         /* [HGM] logo: if either logo is on, reserve space for it */\r
2275         logoHeight =  2*clockSize.cy;\r
2276         leftLogoRect.left   = OUTER_MARGIN;\r
2277         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2278         leftLogoRect.top    = OUTER_MARGIN;\r
2279         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2280 \r
2281         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2282         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2283         rightLogoRect.top    = OUTER_MARGIN;\r
2284         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2285 \r
2286 \r
2287     whiteRect.left = leftLogoRect.right;\r
2288     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2289     whiteRect.top = OUTER_MARGIN;\r
2290     whiteRect.bottom = whiteRect.top + logoHeight;\r
2291 \r
2292     blackRect.right = rightLogoRect.left;\r
2293     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2294     blackRect.top = whiteRect.top;\r
2295     blackRect.bottom = whiteRect.bottom;\r
2296   } else {\r
2297     whiteRect.left = OUTER_MARGIN;\r
2298     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2299     whiteRect.top = OUTER_MARGIN;\r
2300     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2301 \r
2302     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2303     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2304     blackRect.top = whiteRect.top;\r
2305     blackRect.bottom = whiteRect.bottom;\r
2306 \r
2307     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2308   }\r
2309 \r
2310   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2311   if (appData.showButtonBar) {\r
2312     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2313       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2314   } else {\r
2315     messageRect.right = OUTER_MARGIN + boardWidth;\r
2316   }\r
2317   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2318   messageRect.bottom = messageRect.top + messageSize.cy;\r
2319 \r
2320   boardRect.left = OUTER_MARGIN;\r
2321   boardRect.right = boardRect.left + boardWidth;\r
2322   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2323   boardRect.bottom = boardRect.top + boardHeight;\r
2324 \r
2325   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2326   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2327   oldBoardSize = boardSize;\r
2328   oldTinyLayout = tinyLayout;\r
2329   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2330   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2331     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2332   winW *= 1 + twoBoards;\r
2333   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2334   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2335   wpMain.height = winH; //       without disturbing window attachments\r
2336   GetWindowRect(hwndMain, &wrect);\r
2337   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2338                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2339 \r
2340   // [HGM] placement: let attached windows follow size change.\r
2341   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2342   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2343   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2344   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2345   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2346 \r
2347   /* compensate if menu bar wrapped */\r
2348   GetClientRect(hwndMain, &crect);\r
2349   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2350   wpMain.height += offby;\r
2351   switch (flags) {\r
2352   case WMSZ_TOPLEFT:\r
2353     SetWindowPos(hwndMain, NULL, \r
2354                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2355                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2356     break;\r
2357 \r
2358   case WMSZ_TOPRIGHT:\r
2359   case WMSZ_TOP:\r
2360     SetWindowPos(hwndMain, NULL, \r
2361                  wrect.left, wrect.bottom - wpMain.height, \r
2362                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2363     break;\r
2364 \r
2365   case WMSZ_BOTTOMLEFT:\r
2366   case WMSZ_LEFT:\r
2367     SetWindowPos(hwndMain, NULL, \r
2368                  wrect.right - wpMain.width, wrect.top, \r
2369                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2370     break;\r
2371 \r
2372   case WMSZ_BOTTOMRIGHT:\r
2373   case WMSZ_BOTTOM:\r
2374   case WMSZ_RIGHT:\r
2375   default:\r
2376     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2377                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2378     break;\r
2379   }\r
2380 \r
2381   hwndPause = NULL;\r
2382   for (i = 0; i < N_BUTTONS; i++) {\r
2383     if (buttonDesc[i].hwnd != NULL) {\r
2384       DestroyWindow(buttonDesc[i].hwnd);\r
2385       buttonDesc[i].hwnd = NULL;\r
2386     }\r
2387     if (appData.showButtonBar) {\r
2388       buttonDesc[i].hwnd =\r
2389         CreateWindow("BUTTON", buttonDesc[i].label,\r
2390                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2391                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2392                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2393                      (HMENU) buttonDesc[i].id,\r
2394                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2395       if (tinyLayout) {\r
2396         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2397                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2398                     MAKELPARAM(FALSE, 0));\r
2399       }\r
2400       if (buttonDesc[i].id == IDM_Pause)\r
2401         hwndPause = buttonDesc[i].hwnd;\r
2402       buttonDesc[i].wndproc = (WNDPROC)\r
2403         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2404     }\r
2405   }\r
2406   if (gridPen != NULL) DeleteObject(gridPen);\r
2407   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2408   if (premovePen != NULL) DeleteObject(premovePen);\r
2409   if (lineGap != 0) {\r
2410     logbrush.lbStyle = BS_SOLID;\r
2411     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2412     gridPen =\r
2413       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2414                    lineGap, &logbrush, 0, NULL);\r
2415     logbrush.lbColor = highlightSquareColor;\r
2416     highlightPen =\r
2417       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2418                    lineGap, &logbrush, 0, NULL);\r
2419 \r
2420     logbrush.lbColor = premoveHighlightColor; \r
2421     premovePen =\r
2422       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2423                    lineGap, &logbrush, 0, NULL);\r
2424 \r
2425     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2426     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2427       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2428       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2429         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2430       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2431         BOARD_WIDTH * (squareSize + lineGap);\r
2432       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2433     }\r
2434     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2435       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2436       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2437         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2438         lineGap / 2 + (i * (squareSize + lineGap));\r
2439       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2440         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2441       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2442     }\r
2443   }\r
2444 \r
2445   /* [HGM] Licensing requirement */\r
2446 #ifdef GOTHIC\r
2447   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2448 #endif\r
2449 #ifdef FALCON\r
2450   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2451 #endif\r
2452   GothicPopUp( "", VariantNormal);\r
2453 \r
2454 \r
2455 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2456 \r
2457   /* Load piece bitmaps for this board size */\r
2458   for (i=0; i<=2; i++) {\r
2459     for (piece = WhitePawn;\r
2460          (int) piece < (int) BlackPawn;\r
2461          piece = (ChessSquare) ((int) piece + 1)) {\r
2462       if (pieceBitmap[i][piece] != NULL)\r
2463         DeleteObject(pieceBitmap[i][piece]);\r
2464     }\r
2465   }\r
2466 \r
2467   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2468   // Orthodox Chess pieces\r
2469   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2470   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2471   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2472   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2473   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2474   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2475   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2476   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2477   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2478   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2479   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2480   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2481   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2482   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2483   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2484   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2485     // in Shogi, Hijack the unused Queen for Lance\r
2486     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2487     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2488     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2489   } else {\r
2490     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2491     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2492     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2493   }\r
2494 \r
2495   if(squareSize <= 72 && squareSize >= 33) { \r
2496     /* A & C are available in most sizes now */\r
2497     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2498       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2499       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2500       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2501       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2502       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2503       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2504       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2505       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2506       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2507       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2508       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2509       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2510     } else { // Smirf-like\r
2511       if(gameInfo.variant == VariantSChess) {\r
2512         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2513         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2514         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2515       } else {\r
2516         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2517         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2518         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2519       }\r
2520     }\r
2521     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2522       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2523       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2524       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2525     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2526       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2527       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2528       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2529     } else { // WinBoard standard\r
2530       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2531       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2532       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2533     }\r
2534   }\r
2535 \r
2536 \r
2537   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2538     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2539     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2540     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2541     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2542     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2543     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2544     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2545     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2546     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2547     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2548     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2549     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2550     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2551     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2552     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2553     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2554     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2555     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2556     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2557     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2558     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2559     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2560     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2561     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2562     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2563     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2564     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2565     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2566     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2567     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2568 \r
2569     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2570       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2571       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2572       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2573       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2574       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2575       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2576       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2577       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2578       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2579       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2580       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2581       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2582     } else {\r
2583       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2584       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2585       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2586       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2587       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2588       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2589       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2590       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2591       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2592       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2593       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2594       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2595     }\r
2596 \r
2597   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2598     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2599     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2600     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2601     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2602     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2603     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2604     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2605     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2606     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2607     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2608     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2609     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2610     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2611     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2612   }\r
2613 \r
2614 \r
2615   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2616   /* special Shogi support in this size */\r
2617   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2618       for (piece = WhitePawn;\r
2619            (int) piece < (int) BlackPawn;\r
2620            piece = (ChessSquare) ((int) piece + 1)) {\r
2621         if (pieceBitmap[i][piece] != NULL)\r
2622           DeleteObject(pieceBitmap[i][piece]);\r
2623       }\r
2624     }\r
2625   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2626   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2627   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2628   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2629   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2630   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2631   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2632   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2633   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2634   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2635   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2636   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2637   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2638   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2639   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2640   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2641   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2642   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2643   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2644   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2645   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2646   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2647   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2648   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2649   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2650   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2651   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2652   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2653   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2654   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2655   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2656   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2657   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2658   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2659   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2660   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2661   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2662   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2663   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2664   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2665   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2666   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2667   minorSize = 0;\r
2668   }\r
2669 }\r
2670 \r
2671 HBITMAP\r
2672 PieceBitmap(ChessSquare p, int kind)\r
2673 {\r
2674   if ((int) p >= (int) BlackPawn)\r
2675     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2676 \r
2677   return pieceBitmap[kind][(int) p];\r
2678 }\r
2679 \r
2680 /***************************************************************/\r
2681 \r
2682 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2683 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2684 /*\r
2685 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2686 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2687 */\r
2688 \r
2689 VOID\r
2690 SquareToPos(int row, int column, int * x, int * y)\r
2691 {\r
2692   if (flipView) {\r
2693     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2694     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2695   } else {\r
2696     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2697     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2698   }\r
2699 }\r
2700 \r
2701 VOID\r
2702 DrawCoordsOnDC(HDC hdc)\r
2703 {\r
2704   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
2705   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
2706   char str[2] = { NULLCHAR, NULLCHAR };\r
2707   int oldMode, oldAlign, x, y, start, i;\r
2708   HFONT oldFont;\r
2709   HBRUSH oldBrush;\r
2710 \r
2711   if (!appData.showCoords)\r
2712     return;\r
2713 \r
2714   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2715 \r
2716   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2717   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2718   oldAlign = GetTextAlign(hdc);\r
2719   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2720 \r
2721   y = boardRect.top + lineGap;\r
2722   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2723 \r
2724   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2725   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2726     str[0] = files[start + i];\r
2727     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2728     y += squareSize + lineGap;\r
2729   }\r
2730 \r
2731   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2732 \r
2733   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2734   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2735     str[0] = ranks[start + i];\r
2736     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2737     x += squareSize + lineGap;\r
2738   }    \r
2739 \r
2740   SelectObject(hdc, oldBrush);\r
2741   SetBkMode(hdc, oldMode);\r
2742   SetTextAlign(hdc, oldAlign);\r
2743   SelectObject(hdc, oldFont);\r
2744 }\r
2745 \r
2746 VOID\r
2747 DrawGridOnDC(HDC hdc)\r
2748 {\r
2749   HPEN oldPen;\r
2750  \r
2751   if (lineGap != 0) {\r
2752     oldPen = SelectObject(hdc, gridPen);\r
2753     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2754     SelectObject(hdc, oldPen);\r
2755   }\r
2756 }\r
2757 \r
2758 #define HIGHLIGHT_PEN 0\r
2759 #define PREMOVE_PEN   1\r
2760 \r
2761 VOID\r
2762 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2763 {\r
2764   int x1, y1;\r
2765   HPEN oldPen, hPen;\r
2766   if (lineGap == 0) return;\r
2767   if (flipView) {\r
2768     x1 = boardRect.left +\r
2769       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2770     y1 = boardRect.top +\r
2771       lineGap/2 + y * (squareSize + lineGap);\r
2772   } else {\r
2773     x1 = boardRect.left +\r
2774       lineGap/2 + x * (squareSize + lineGap);\r
2775     y1 = boardRect.top +\r
2776       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2777   }\r
2778   hPen = pen ? premovePen : highlightPen;\r
2779   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2780   MoveToEx(hdc, x1, y1, NULL);\r
2781   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2782   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2783   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2784   LineTo(hdc, x1, y1);\r
2785   SelectObject(hdc, oldPen);\r
2786 }\r
2787 \r
2788 VOID\r
2789 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2790 {\r
2791   int i;\r
2792   for (i=0; i<2; i++) {\r
2793     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2794       DrawHighlightOnDC(hdc, TRUE,\r
2795                         h->sq[i].x, h->sq[i].y,\r
2796                         pen);\r
2797   }\r
2798 }\r
2799 \r
2800 /* Note: sqcolor is used only in monoMode */\r
2801 /* Note that this code is largely duplicated in woptions.c,\r
2802    function DrawSampleSquare, so that needs to be updated too */\r
2803 VOID\r
2804 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2805 {\r
2806   HBITMAP oldBitmap;\r
2807   HBRUSH oldBrush;\r
2808   int tmpSize;\r
2809 \r
2810   if (appData.blindfold) return;\r
2811 \r
2812   /* [AS] Use font-based pieces if needed */\r
2813   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2814     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2815     CreatePiecesFromFont();\r
2816 \r
2817     if( fontBitmapSquareSize == squareSize ) {\r
2818         int index = TranslatePieceToFontPiece(piece);\r
2819 \r
2820         SelectObject( tmphdc, hPieceMask[ index ] );\r
2821 \r
2822       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2823         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2824       else\r
2825         BitBlt( hdc,\r
2826             x, y,\r
2827             squareSize, squareSize,\r
2828             tmphdc,\r
2829             0, 0,\r
2830             SRCAND );\r
2831 \r
2832         SelectObject( tmphdc, hPieceFace[ index ] );\r
2833 \r
2834       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2835         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2836       else\r
2837         BitBlt( hdc,\r
2838             x, y,\r
2839             squareSize, squareSize,\r
2840             tmphdc,\r
2841             0, 0,\r
2842             SRCPAINT );\r
2843 \r
2844         return;\r
2845     }\r
2846   }\r
2847 \r
2848   if (appData.monoMode) {\r
2849     SelectObject(tmphdc, PieceBitmap(piece, \r
2850       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2851     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2852            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2853   } else {\r
2854     tmpSize = squareSize;\r
2855     if(minorSize &&\r
2856         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2857          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2858       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2859       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2860       x += (squareSize - minorSize)>>1;\r
2861       y += squareSize - minorSize - 2;\r
2862       tmpSize = minorSize;\r
2863     }\r
2864     if (color || appData.allWhite ) {\r
2865       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2866       if( color )\r
2867               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2868       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2869       if(appData.upsideDown && color==flipView)\r
2870         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2871       else\r
2872         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2873       /* Use black for outline of white pieces */\r
2874       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2875       if(appData.upsideDown && color==flipView)\r
2876         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2877       else\r
2878         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2879     } else {\r
2880       /* Use square color for details of black pieces */\r
2881       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2882       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2883       if(appData.upsideDown && !flipView)\r
2884         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2885       else\r
2886         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2887     }\r
2888     SelectObject(hdc, oldBrush);\r
2889     SelectObject(tmphdc, oldBitmap);\r
2890   }\r
2891 }\r
2892 \r
2893 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2894 int GetBackTextureMode( int algo )\r
2895 {\r
2896     int result = BACK_TEXTURE_MODE_DISABLED;\r
2897 \r
2898     switch( algo ) \r
2899     {\r
2900         case BACK_TEXTURE_MODE_PLAIN:\r
2901             result = 1; /* Always use identity map */\r
2902             break;\r
2903         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2904             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2905             break;\r
2906     }\r
2907 \r
2908     return result;\r
2909 }\r
2910 \r
2911 /* \r
2912     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2913     to handle redraws cleanly (as random numbers would always be different).\r
2914 */\r
2915 VOID RebuildTextureSquareInfo()\r
2916 {\r
2917     BITMAP bi;\r
2918     int lite_w = 0;\r
2919     int lite_h = 0;\r
2920     int dark_w = 0;\r
2921     int dark_h = 0;\r
2922     int row;\r
2923     int col;\r
2924 \r
2925     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2926 \r
2927     if( liteBackTexture != NULL ) {\r
2928         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2929             lite_w = bi.bmWidth;\r
2930             lite_h = bi.bmHeight;\r
2931         }\r
2932     }\r
2933 \r
2934     if( darkBackTexture != NULL ) {\r
2935         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2936             dark_w = bi.bmWidth;\r
2937             dark_h = bi.bmHeight;\r
2938         }\r
2939     }\r
2940 \r
2941     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2942         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2943             if( (col + row) & 1 ) {\r
2944                 /* Lite square */\r
2945                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2946                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2947                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2948                   else\r
2949                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2950                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2951                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2952                   else\r
2953                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2954                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2955                 }\r
2956             }\r
2957             else {\r
2958                 /* Dark square */\r
2959                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2960                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2961                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2962                   else\r
2963                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2964                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2965                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2966                   else\r
2967                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2968                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2969                 }\r
2970             }\r
2971         }\r
2972     }\r
2973 }\r
2974 \r
2975 /* [AS] Arrow highlighting support */\r
2976 \r
2977 static double A_WIDTH = 5; /* Width of arrow body */\r
2978 \r
2979 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2980 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2981 \r
2982 static double Sqr( double x )\r
2983 {\r
2984     return x*x;\r
2985 }\r
2986 \r
2987 static int Round( double x )\r
2988 {\r
2989     return (int) (x + 0.5);\r
2990 }\r
2991 \r
2992 /* Draw an arrow between two points using current settings */\r
2993 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2994 {\r
2995     POINT arrow[7];\r
2996     double dx, dy, j, k, x, y;\r
2997 \r
2998     if( d_x == s_x ) {\r
2999         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3000 \r
3001         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3002         arrow[0].y = s_y;\r
3003 \r
3004         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3005         arrow[1].y = d_y - h;\r
3006 \r
3007         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3008         arrow[2].y = d_y - h;\r
3009 \r
3010         arrow[3].x = d_x;\r
3011         arrow[3].y = d_y;\r
3012 \r
3013         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3014         arrow[5].y = d_y - h;\r
3015 \r
3016         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3017         arrow[4].y = d_y - h;\r
3018 \r
3019         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3020         arrow[6].y = s_y;\r
3021     }\r
3022     else if( d_y == s_y ) {\r
3023         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3024 \r
3025         arrow[0].x = s_x;\r
3026         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3027 \r
3028         arrow[1].x = d_x - w;\r
3029         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3030 \r
3031         arrow[2].x = d_x - w;\r
3032         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3033 \r
3034         arrow[3].x = d_x;\r
3035         arrow[3].y = d_y;\r
3036 \r
3037         arrow[5].x = d_x - w;\r
3038         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3039 \r
3040         arrow[4].x = d_x - w;\r
3041         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3042 \r
3043         arrow[6].x = s_x;\r
3044         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3045     }\r
3046     else {\r
3047         /* [AS] Needed a lot of paper for this! :-) */\r
3048         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3049         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3050   \r
3051         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3052 \r
3053         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3054 \r
3055         x = s_x;\r
3056         y = s_y;\r
3057 \r
3058         arrow[0].x = Round(x - j);\r
3059         arrow[0].y = Round(y + j*dx);\r
3060 \r
3061         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3062         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3063 \r
3064         if( d_x > s_x ) {\r
3065             x = (double) d_x - k;\r
3066             y = (double) d_y - k*dy;\r
3067         }\r
3068         else {\r
3069             x = (double) d_x + k;\r
3070             y = (double) d_y + k*dy;\r
3071         }\r
3072 \r
3073         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3074 \r
3075         arrow[6].x = Round(x - j);\r
3076         arrow[6].y = Round(y + j*dx);\r
3077 \r
3078         arrow[2].x = Round(arrow[6].x + 2*j);\r
3079         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3080 \r
3081         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3082         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3083 \r
3084         arrow[4].x = d_x;\r
3085         arrow[4].y = d_y;\r
3086 \r
3087         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3088         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3089     }\r
3090 \r
3091     Polygon( hdc, arrow, 7 );\r
3092 }\r
3093 \r
3094 /* [AS] Draw an arrow between two squares */\r
3095 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3096 {\r
3097     int s_x, s_y, d_x, d_y;\r
3098     HPEN hpen;\r
3099     HPEN holdpen;\r
3100     HBRUSH hbrush;\r
3101     HBRUSH holdbrush;\r
3102     LOGBRUSH stLB;\r
3103 \r
3104     if( s_col == d_col && s_row == d_row ) {\r
3105         return;\r
3106     }\r
3107 \r
3108     /* Get source and destination points */\r
3109     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3110     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3111 \r
3112     if( d_y > s_y ) {\r
3113         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3114     }\r
3115     else if( d_y < s_y ) {\r
3116         d_y += squareSize / 2 + squareSize / 4;\r
3117     }\r
3118     else {\r
3119         d_y += squareSize / 2;\r
3120     }\r
3121 \r
3122     if( d_x > s_x ) {\r
3123         d_x += squareSize / 2 - squareSize / 4;\r
3124     }\r
3125     else if( d_x < s_x ) {\r
3126         d_x += squareSize / 2 + squareSize / 4;\r
3127     }\r
3128     else {\r
3129         d_x += squareSize / 2;\r
3130     }\r
3131 \r
3132     s_x += squareSize / 2;\r
3133     s_y += squareSize / 2;\r
3134 \r
3135     /* Adjust width */\r
3136     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3137 \r
3138     /* Draw */\r
3139     stLB.lbStyle = BS_SOLID;\r
3140     stLB.lbColor = appData.highlightArrowColor;\r
3141     stLB.lbHatch = 0;\r
3142 \r
3143     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3144     holdpen = SelectObject( hdc, hpen );\r
3145     hbrush = CreateBrushIndirect( &stLB );\r
3146     holdbrush = SelectObject( hdc, hbrush );\r
3147 \r
3148     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3149 \r
3150     SelectObject( hdc, holdpen );\r
3151     SelectObject( hdc, holdbrush );\r
3152     DeleteObject( hpen );\r
3153     DeleteObject( hbrush );\r
3154 }\r
3155 \r
3156 BOOL HasHighlightInfo()\r
3157 {\r
3158     BOOL result = FALSE;\r
3159 \r
3160     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3161         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3162     {\r
3163         result = TRUE;\r
3164     }\r
3165 \r
3166     return result;\r
3167 }\r
3168 \r
3169 BOOL IsDrawArrowEnabled()\r
3170 {\r
3171     BOOL result = FALSE;\r
3172 \r
3173     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3174         result = TRUE;\r
3175     }\r
3176 \r
3177     return result;\r
3178 }\r
3179 \r
3180 VOID DrawArrowHighlight( HDC hdc )\r
3181 {\r
3182     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3183         DrawArrowBetweenSquares( hdc,\r
3184             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3185             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3186     }\r
3187 }\r
3188 \r
3189 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3190 {\r
3191     HRGN result = NULL;\r
3192 \r
3193     if( HasHighlightInfo() ) {\r
3194         int x1, y1, x2, y2;\r
3195         int sx, sy, dx, dy;\r
3196 \r
3197         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3198         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3199 \r
3200         sx = MIN( x1, x2 );\r
3201         sy = MIN( y1, y2 );\r
3202         dx = MAX( x1, x2 ) + squareSize;\r
3203         dy = MAX( y1, y2 ) + squareSize;\r
3204 \r
3205         result = CreateRectRgn( sx, sy, dx, dy );\r
3206     }\r
3207 \r
3208     return result;\r
3209 }\r
3210 \r
3211 /*\r
3212     Warning: this function modifies the behavior of several other functions. \r
3213     \r
3214     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3215     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3216     repaint is scattered all over the place, which is not good for features such as\r
3217     "arrow highlighting" that require a full repaint of the board.\r
3218 \r
3219     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3220     user interaction, when speed is not so important) but especially to avoid errors\r
3221     in the displayed graphics.\r
3222 \r
3223     In such patched places, I always try refer to this function so there is a single\r
3224     place to maintain knowledge.\r
3225     \r
3226     To restore the original behavior, just return FALSE unconditionally.\r
3227 */\r
3228 BOOL IsFullRepaintPreferrable()\r
3229 {\r
3230     BOOL result = FALSE;\r
3231 \r
3232     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3233         /* Arrow may appear on the board */\r
3234         result = TRUE;\r
3235     }\r
3236 \r
3237     return result;\r
3238 }\r
3239 \r
3240 /* \r
3241     This function is called by DrawPosition to know whether a full repaint must\r
3242     be forced or not.\r
3243 \r
3244     Only DrawPosition may directly call this function, which makes use of \r
3245     some state information. Other function should call DrawPosition specifying \r
3246     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3247 */\r
3248 BOOL DrawPositionNeedsFullRepaint()\r
3249 {\r
3250     BOOL result = FALSE;\r
3251 \r
3252     /* \r
3253         Probably a slightly better policy would be to trigger a full repaint\r
3254         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3255         but animation is fast enough that it's difficult to notice.\r
3256     */\r
3257     if( animInfo.piece == EmptySquare ) {\r
3258         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3259             result = TRUE;\r
3260         }\r
3261     }\r
3262 \r
3263     return result;\r
3264 }\r
3265 \r
3266 VOID\r
3267 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3268 {\r
3269   int row, column, x, y, square_color, piece_color;\r
3270   ChessSquare piece;\r
3271   HBRUSH oldBrush;\r
3272   HDC texture_hdc = NULL;\r
3273 \r
3274   /* [AS] Initialize background textures if needed */\r
3275   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3276       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3277       if( backTextureSquareSize != squareSize \r
3278        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3279           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3280           backTextureSquareSize = squareSize;\r
3281           RebuildTextureSquareInfo();\r
3282       }\r
3283 \r
3284       texture_hdc = CreateCompatibleDC( hdc );\r
3285   }\r
3286 \r
3287   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3288     for (column = 0; column < BOARD_WIDTH; column++) {\r
3289   \r
3290       SquareToPos(row, column, &x, &y);\r
3291 \r
3292       piece = board[row][column];\r
3293 \r
3294       square_color = ((column + row) % 2) == 1;\r
3295       if( gameInfo.variant == VariantXiangqi ) {\r
3296           square_color = !InPalace(row, column);\r
3297           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3298           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3299       }\r
3300       piece_color = (int) piece < (int) BlackPawn;\r
3301 \r
3302 \r
3303       /* [HGM] holdings file: light square or black */\r
3304       if(column == BOARD_LEFT-2) {\r
3305             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3306                 square_color = 1;\r
3307             else {\r
3308                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3309                 continue;\r
3310             }\r
3311       } else\r
3312       if(column == BOARD_RGHT + 1 ) {\r
3313             if( row < gameInfo.holdingsSize )\r
3314                 square_color = 1;\r
3315             else {\r
3316                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3317                 continue;\r
3318             }\r
3319       }\r
3320       if(column == BOARD_LEFT-1 ) /* left align */\r
3321             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3322       else if( column == BOARD_RGHT) /* right align */\r
3323             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3324       else\r
3325       if (appData.monoMode) {\r
3326         if (piece == EmptySquare) {\r
3327           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3328                  square_color ? WHITENESS : BLACKNESS);\r
3329         } else {\r
3330           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3331         }\r
3332       } \r
3333       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3334           /* [AS] Draw the square using a texture bitmap */\r
3335           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3336           int r = row, c = column; // [HGM] do not flip board in flipView\r
3337           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3338 \r
3339           DrawTile( x, y, \r
3340               squareSize, squareSize, \r
3341               hdc, \r
3342               texture_hdc,\r
3343               backTextureSquareInfo[r][c].mode,\r
3344               backTextureSquareInfo[r][c].x,\r
3345               backTextureSquareInfo[r][c].y );\r
3346 \r
3347           SelectObject( texture_hdc, hbm );\r
3348 \r
3349           if (piece != EmptySquare) {\r
3350               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3351           }\r
3352       }\r
3353       else {\r
3354         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3355 \r
3356         oldBrush = SelectObject(hdc, brush );\r
3357         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3358         SelectObject(hdc, oldBrush);\r
3359         if (piece != EmptySquare)\r
3360           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3361       }\r
3362     }\r
3363   }\r
3364 \r
3365   if( texture_hdc != NULL ) {\r
3366     DeleteDC( texture_hdc );\r
3367   }\r
3368 }\r
3369 \r
3370 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3371 void fputDW(FILE *f, int x)\r
3372 {\r
3373         fputc(x     & 255, f);\r
3374         fputc(x>>8  & 255, f);\r
3375         fputc(x>>16 & 255, f);\r
3376         fputc(x>>24 & 255, f);\r
3377 }\r
3378 \r
3379 #define MAX_CLIPS 200   /* more than enough */\r
3380 \r
3381 VOID\r
3382 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3383 {\r
3384 //  HBITMAP bufferBitmap;\r
3385   BITMAP bi;\r
3386 //  RECT Rect;\r
3387   HDC tmphdc;\r
3388   HBITMAP hbm;\r
3389   int w = 100, h = 50;\r
3390 \r
3391   if(logo == NULL) return;\r
3392 //  GetClientRect(hwndMain, &Rect);\r
3393 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3394 //                                      Rect.bottom-Rect.top+1);\r
3395   tmphdc = CreateCompatibleDC(hdc);\r
3396   hbm = SelectObject(tmphdc, logo);\r
3397   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3398             w = bi.bmWidth;\r
3399             h = bi.bmHeight;\r
3400   }\r
3401   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3402                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3403   SelectObject(tmphdc, hbm);\r
3404   DeleteDC(tmphdc);\r
3405 }\r
3406 \r
3407 VOID\r
3408 DisplayLogos()\r
3409 {\r
3410   if(logoHeight) {\r
3411         HDC hdc = GetDC(hwndMain);\r
3412         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3413         if(appData.autoLogo) {\r
3414           \r
3415           switch(gameMode) { // pick logos based on game mode\r
3416             case IcsObserving:\r
3417                 whiteLogo = second.programLogo; // ICS logo\r
3418                 blackLogo = second.programLogo;\r
3419             default:\r
3420                 break;\r
3421             case IcsPlayingWhite:\r
3422                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3423                 blackLogo = second.programLogo; // ICS logo\r
3424                 break;\r
3425             case IcsPlayingBlack:\r
3426                 whiteLogo = second.programLogo; // ICS logo\r
3427                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3428                 break;\r
3429             case TwoMachinesPlay:\r
3430                 if(first.twoMachinesColor[0] == 'b') {\r
3431                     whiteLogo = second.programLogo;\r
3432                     blackLogo = first.programLogo;\r
3433                 }\r
3434                 break;\r
3435             case MachinePlaysWhite:\r
3436                 blackLogo = userLogo;\r
3437                 break;\r
3438             case MachinePlaysBlack:\r
3439                 whiteLogo = userLogo;\r
3440                 blackLogo = first.programLogo;\r
3441           }\r
3442         }\r
3443         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3444         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3445         ReleaseDC(hwndMain, hdc);\r
3446   }\r
3447 }\r
3448 \r
3449 static HDC hdcSeek;\r
3450 \r
3451 // [HGM] seekgraph\r
3452 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3453 {\r
3454     POINT stPt;\r
3455     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3456     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3457     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3458     SelectObject( hdcSeek, hp );\r
3459 }\r
3460 \r
3461 // front-end wrapper for drawing functions to do rectangles\r
3462 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3463 {\r
3464     HPEN hp;\r
3465     RECT rc;\r
3466 \r
3467     if (hdcSeek == NULL) {\r
3468     hdcSeek = GetDC(hwndMain);\r
3469       if (!appData.monoMode) {\r
3470         SelectPalette(hdcSeek, hPal, FALSE);\r
3471         RealizePalette(hdcSeek);\r
3472       }\r
3473     }\r
3474     hp = SelectObject( hdcSeek, gridPen );\r
3475     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3476     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3477     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3478     SelectObject( hdcSeek, hp );\r
3479 }\r
3480 \r
3481 // front-end wrapper for putting text in graph\r
3482 void DrawSeekText(char *buf, int x, int y)\r
3483 {\r
3484         SIZE stSize;\r
3485         SetBkMode( hdcSeek, TRANSPARENT );\r
3486         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3487         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3488 }\r
3489 \r
3490 void DrawSeekDot(int x, int y, int color)\r
3491 {\r
3492         int square = color & 0x80;\r
3493         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3494                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3495         color &= 0x7F;\r
3496         if(square)\r
3497             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3498                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3499         else\r
3500             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3501                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3502             SelectObject(hdcSeek, oldBrush);\r
3503 }\r
3504 \r
3505 VOID\r
3506 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3507 {\r
3508   static Board lastReq[2], lastDrawn[2];\r
3509   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3510   static int lastDrawnFlipView = 0;\r
3511   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3512   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3513   HDC tmphdc;\r
3514   HDC hdcmem;\r
3515   HBITMAP bufferBitmap;\r
3516   HBITMAP oldBitmap;\r
3517   RECT Rect;\r
3518   HRGN clips[MAX_CLIPS];\r
3519   ChessSquare dragged_piece = EmptySquare;\r
3520   int nr = twoBoards*partnerUp;\r
3521 \r
3522   /* I'm undecided on this - this function figures out whether a full\r
3523    * repaint is necessary on its own, so there's no real reason to have the\r
3524    * caller tell it that.  I think this can safely be set to FALSE - but\r
3525    * if we trust the callers not to request full repaints unnessesarily, then\r
3526    * we could skip some clipping work.  In other words, only request a full\r
3527    * redraw when the majority of pieces have changed positions (ie. flip, \r
3528    * gamestart and similar)  --Hawk\r
3529    */\r
3530   Boolean fullrepaint = repaint;\r
3531 \r
3532   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3533 \r
3534   if( DrawPositionNeedsFullRepaint() ) {\r
3535       fullrepaint = TRUE;\r
3536   }\r
3537 \r
3538   if (board == NULL) {\r
3539     if (!lastReqValid[nr]) {\r
3540       return;\r
3541     }\r
3542     board = lastReq[nr];\r
3543   } else {\r
3544     CopyBoard(lastReq[nr], board);\r
3545     lastReqValid[nr] = 1;\r
3546   }\r
3547 \r
3548   if (doingSizing) {\r
3549     return;\r
3550   }\r
3551 \r
3552   if (IsIconic(hwndMain)) {\r
3553     return;\r
3554   }\r
3555 \r
3556   if (hdc == NULL) {\r
3557     hdc = GetDC(hwndMain);\r
3558     if (!appData.monoMode) {\r
3559       SelectPalette(hdc, hPal, FALSE);\r
3560       RealizePalette(hdc);\r
3561     }\r
3562     releaseDC = TRUE;\r
3563   } else {\r
3564     releaseDC = FALSE;\r
3565   }\r
3566 \r
3567   /* Create some work-DCs */\r
3568   hdcmem = CreateCompatibleDC(hdc);\r
3569   tmphdc = CreateCompatibleDC(hdc);\r
3570 \r
3571   /* If dragging is in progress, we temporarely remove the piece */\r
3572   /* [HGM] or temporarily decrease count if stacked              */\r
3573   /*       !! Moved to before board compare !!                   */\r
3574   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3575     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3576     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3577             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3578         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3579     } else \r
3580     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3581             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3582         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3583     } else \r
3584         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3585   }\r
3586 \r
3587   /* Figure out which squares need updating by comparing the \r
3588    * newest board with the last drawn board and checking if\r
3589    * flipping has changed.\r
3590    */\r
3591   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3592     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3593       for (column = 0; column < BOARD_WIDTH; column++) {\r
3594         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3595           SquareToPos(row, column, &x, &y);\r
3596           clips[num_clips++] =\r
3597             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3598         }\r
3599       }\r
3600     }\r
3601    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3602     for (i=0; i<2; i++) {\r
3603       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3604           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3605         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3606             lastDrawnHighlight.sq[i].y >= 0) {\r
3607           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3608                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3609           clips[num_clips++] =\r
3610             CreateRectRgn(x - lineGap, y - lineGap, \r
3611                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3612         }\r
3613         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3614           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3615           clips[num_clips++] =\r
3616             CreateRectRgn(x - lineGap, y - lineGap, \r
3617                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3618         }\r
3619       }\r
3620     }\r
3621     for (i=0; i<2; i++) {\r
3622       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3623           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3624         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3625             lastDrawnPremove.sq[i].y >= 0) {\r
3626           SquareToPos(lastDrawnPremove.sq[i].y,\r
3627                       lastDrawnPremove.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         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3633             premoveHighlightInfo.sq[i].y >= 0) {\r
3634           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3635                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3636           clips[num_clips++] =\r
3637             CreateRectRgn(x - lineGap, y - lineGap, \r
3638                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3639         }\r
3640       }\r
3641     }\r
3642    } else { // nr == 1\r
3643         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3644         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3645         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3646         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3647       for (i=0; i<2; i++) {\r
3648         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3649             partnerHighlightInfo.sq[i].y >= 0) {\r
3650           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3651                       partnerHighlightInfo.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         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3657             oldPartnerHighlight.sq[i].y >= 0) {\r
3658           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3659                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3660           clips[num_clips++] =\r
3661             CreateRectRgn(x - lineGap, y - lineGap, \r
3662                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3663         }\r
3664       }\r
3665    }\r
3666   } else {\r
3667     fullrepaint = TRUE;\r
3668   }\r
3669 \r
3670   /* Create a buffer bitmap - this is the actual bitmap\r
3671    * being written to.  When all the work is done, we can\r
3672    * copy it to the real DC (the screen).  This avoids\r
3673    * the problems with flickering.\r
3674    */\r
3675   GetClientRect(hwndMain, &Rect);\r
3676   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3677                                         Rect.bottom-Rect.top+1);\r
3678   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3679   if (!appData.monoMode) {\r
3680     SelectPalette(hdcmem, hPal, FALSE);\r
3681   }\r
3682 \r
3683   /* Create clips for dragging */\r
3684   if (!fullrepaint) {\r
3685     if (dragInfo.from.x >= 0) {\r
3686       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3687       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3688     }\r
3689     if (dragInfo.start.x >= 0) {\r
3690       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3691       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3692     }\r
3693     if (dragInfo.pos.x >= 0) {\r
3694       x = dragInfo.pos.x - squareSize / 2;\r
3695       y = dragInfo.pos.y - squareSize / 2;\r
3696       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3697     }\r
3698     if (dragInfo.lastpos.x >= 0) {\r
3699       x = dragInfo.lastpos.x - squareSize / 2;\r
3700       y = dragInfo.lastpos.y - squareSize / 2;\r
3701       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3702     }\r
3703   }\r
3704 \r
3705   /* Are we animating a move?  \r
3706    * If so, \r
3707    *   - remove the piece from the board (temporarely)\r
3708    *   - calculate the clipping region\r
3709    */\r
3710   if (!fullrepaint) {\r
3711     if (animInfo.piece != EmptySquare) {\r
3712       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3713       x = boardRect.left + animInfo.lastpos.x;\r
3714       y = boardRect.top + animInfo.lastpos.y;\r
3715       x2 = boardRect.left + animInfo.pos.x;\r
3716       y2 = boardRect.top + animInfo.pos.y;\r
3717       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3718       /* Slight kludge.  The real problem is that after AnimateMove is\r
3719          done, the position on the screen does not match lastDrawn.\r
3720          This currently causes trouble only on e.p. captures in\r
3721          atomic, where the piece moves to an empty square and then\r
3722          explodes.  The old and new positions both had an empty square\r
3723          at the destination, but animation has drawn a piece there and\r
3724          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3725       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3726     }\r
3727   }\r
3728 \r
3729   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3730   if (num_clips == 0)\r
3731     fullrepaint = TRUE;\r
3732 \r
3733   /* Set clipping on the memory DC */\r
3734   if (!fullrepaint) {\r
3735     SelectClipRgn(hdcmem, clips[0]);\r
3736     for (x = 1; x < num_clips; x++) {\r
3737       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3738         abort();  // this should never ever happen!\r
3739     }\r
3740   }\r
3741 \r
3742   /* Do all the drawing to the memory DC */\r
3743   if(explodeInfo.radius) { // [HGM] atomic\r
3744         HBRUSH oldBrush;\r
3745         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3746         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3747         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3748         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3749         x += squareSize/2;\r
3750         y += squareSize/2;\r
3751         if(!fullrepaint) {\r
3752           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3753           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3754         }\r
3755         DrawGridOnDC(hdcmem);\r
3756         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3757         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3758         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3759         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3760         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3761         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3762         SelectObject(hdcmem, oldBrush);\r
3763   } else {\r
3764     DrawGridOnDC(hdcmem);\r
3765     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3766         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3767         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3768     } else {\r
3769         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3770         oldPartnerHighlight = partnerHighlightInfo;\r
3771     }\r
3772     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3773   }\r
3774   if(nr == 0) // [HGM] dual: markers only on left board\r
3775   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3776     for (column = 0; column < BOARD_WIDTH; column++) {\r
3777         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3778             HBRUSH oldBrush = SelectObject(hdcmem, \r
3779                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3780             SquareToPos(row, column, &x, &y);\r
3781             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3782                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3783             SelectObject(hdcmem, oldBrush);\r
3784         }\r
3785     }\r
3786   }\r
3787 \r
3788   if( appData.highlightMoveWithArrow ) {\r
3789     DrawArrowHighlight(hdcmem);\r
3790   }\r
3791 \r
3792   DrawCoordsOnDC(hdcmem);\r
3793 \r
3794   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3795                  /* to make sure lastDrawn contains what is actually drawn */\r
3796 \r
3797   /* Put the dragged piece back into place and draw it (out of place!) */\r
3798     if (dragged_piece != EmptySquare) {\r
3799     /* [HGM] or restack */\r
3800     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3801                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3802     else\r
3803     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3804                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3805     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3806     x = dragInfo.pos.x - squareSize / 2;\r
3807     y = dragInfo.pos.y - squareSize / 2;\r
3808     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3809                   ((int) dragInfo.piece < (int) BlackPawn), \r
3810                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3811   }   \r
3812   \r
3813   /* Put the animated piece back into place and draw it */\r
3814   if (animInfo.piece != EmptySquare) {\r
3815     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3816     x = boardRect.left + animInfo.pos.x;\r
3817     y = boardRect.top + animInfo.pos.y;\r
3818     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3819                   ((int) animInfo.piece < (int) BlackPawn),\r
3820                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3821   }\r
3822 \r
3823   /* Release the bufferBitmap by selecting in the old bitmap \r
3824    * and delete the memory DC\r
3825    */\r
3826   SelectObject(hdcmem, oldBitmap);\r
3827   DeleteDC(hdcmem);\r
3828 \r
3829   /* Set clipping on the target DC */\r
3830   if (!fullrepaint) {\r
3831     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3832         RECT rect;\r
3833         GetRgnBox(clips[x], &rect);\r
3834         DeleteObject(clips[x]);\r
3835         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3836                           rect.right + wpMain.width/2, rect.bottom);\r
3837     }\r
3838     SelectClipRgn(hdc, clips[0]);\r
3839     for (x = 1; x < num_clips; x++) {\r
3840       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3841         abort();   // this should never ever happen!\r
3842     } \r
3843   }\r
3844 \r
3845   /* Copy the new bitmap onto the screen in one go.\r
3846    * This way we avoid any flickering\r
3847    */\r
3848   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3849   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3850          boardRect.right - boardRect.left,\r
3851          boardRect.bottom - boardRect.top,\r
3852          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3853   if(saveDiagFlag) { \r
3854     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3855     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3856 \r
3857     GetObject(bufferBitmap, sizeof(b), &b);\r
3858     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3859         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3860         bih.biWidth = b.bmWidth;\r
3861         bih.biHeight = b.bmHeight;\r
3862         bih.biPlanes = 1;\r
3863         bih.biBitCount = b.bmBitsPixel;\r
3864         bih.biCompression = 0;\r
3865         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3866         bih.biXPelsPerMeter = 0;\r
3867         bih.biYPelsPerMeter = 0;\r
3868         bih.biClrUsed = 0;\r
3869         bih.biClrImportant = 0;\r
3870 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3871 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3872         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3873 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3874 \r
3875         wb = b.bmWidthBytes;\r
3876         // count colors\r
3877         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3878                 int k = ((int*) pData)[i];\r
3879                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3880                 if(j >= 16) break;\r
3881                 color[j] = k;\r
3882                 if(j >= nrColors) nrColors = j+1;\r
3883         }\r
3884         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3885                 INT p = 0;\r
3886                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3887                     for(w=0; w<(wb>>2); w+=2) {\r
3888                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3889                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3890                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3891                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3892                         pData[p++] = m | j<<4;\r
3893                     }\r
3894                     while(p&3) pData[p++] = 0;\r
3895                 }\r
3896                 fac = 3;\r
3897                 wb = ((wb+31)>>5)<<2;\r
3898         }\r
3899         // write BITMAPFILEHEADER\r
3900         fprintf(diagFile, "BM");\r
3901         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3902         fputDW(diagFile, 0);\r
3903         fputDW(diagFile, 0x36 + (fac?64:0));\r
3904         // write BITMAPINFOHEADER\r
3905         fputDW(diagFile, 40);\r
3906         fputDW(diagFile, b.bmWidth);\r
3907         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3908         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3909         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3910         fputDW(diagFile, 0);\r
3911         fputDW(diagFile, 0);\r
3912         fputDW(diagFile, 0);\r
3913         fputDW(diagFile, 0);\r
3914         fputDW(diagFile, 0);\r
3915         fputDW(diagFile, 0);\r
3916         // write color table\r
3917         if(fac)\r
3918         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3919         // write bitmap data\r
3920         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3921                 fputc(pData[i], diagFile);\r
3922         free(pData);\r
3923      }\r
3924   }\r
3925 \r
3926   SelectObject(tmphdc, oldBitmap);\r
3927 \r
3928   /* Massive cleanup */\r
3929   for (x = 0; x < num_clips; x++)\r
3930     DeleteObject(clips[x]);\r
3931 \r
3932   DeleteDC(tmphdc);\r
3933   DeleteObject(bufferBitmap);\r
3934 \r
3935   if (releaseDC) \r
3936     ReleaseDC(hwndMain, hdc);\r
3937   \r
3938   if (lastDrawnFlipView != flipView && nr == 0) {\r
3939     if (flipView)\r
3940       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3941     else\r
3942       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3943   }\r
3944 \r
3945 /*  CopyBoard(lastDrawn, board);*/\r
3946   lastDrawnHighlight = highlightInfo;\r
3947   lastDrawnPremove   = premoveHighlightInfo;\r
3948   lastDrawnFlipView = flipView;\r
3949   lastDrawnValid[nr] = 1;\r
3950 }\r
3951 \r
3952 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3953 int\r
3954 SaveDiagram(f)\r
3955      FILE *f;\r
3956 {\r
3957     saveDiagFlag = 1; diagFile = f;\r
3958     HDCDrawPosition(NULL, TRUE, NULL);\r
3959     saveDiagFlag = 0;\r
3960 \r
3961     fclose(f);\r
3962     return TRUE;\r
3963 }\r
3964 \r
3965 \r
3966 /*---------------------------------------------------------------------------*\\r
3967 | CLIENT PAINT PROCEDURE\r
3968 |   This is the main event-handler for the WM_PAINT message.\r
3969 |\r
3970 \*---------------------------------------------------------------------------*/\r
3971 VOID\r
3972 PaintProc(HWND hwnd)\r
3973 {\r
3974   HDC         hdc;\r
3975   PAINTSTRUCT ps;\r
3976   HFONT       oldFont;\r
3977 \r
3978   if((hdc = BeginPaint(hwnd, &ps))) {\r
3979     if (IsIconic(hwnd)) {\r
3980       DrawIcon(hdc, 2, 2, iconCurrent);\r
3981     } else {\r
3982       if (!appData.monoMode) {\r
3983         SelectPalette(hdc, hPal, FALSE);\r
3984         RealizePalette(hdc);\r
3985       }\r
3986       HDCDrawPosition(hdc, 1, NULL);\r
3987       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3988         flipView = !flipView; partnerUp = !partnerUp;\r
3989         HDCDrawPosition(hdc, 1, NULL);\r
3990         flipView = !flipView; partnerUp = !partnerUp;\r
3991       }\r
3992       oldFont =\r
3993         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3994       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3995                  ETO_CLIPPED|ETO_OPAQUE,\r
3996                  &messageRect, messageText, strlen(messageText), NULL);\r
3997       SelectObject(hdc, oldFont);\r
3998       DisplayBothClocks();\r
3999       DisplayLogos();\r
4000     }\r
4001     EndPaint(hwnd,&ps);\r
4002   }\r
4003 \r
4004   return;\r
4005 }\r
4006 \r
4007 \r
4008 /*\r
4009  * If the user selects on a border boundary, return -1; if off the board,\r
4010  *   return -2.  Otherwise map the event coordinate to the square.\r
4011  * The offset boardRect.left or boardRect.top must already have been\r
4012  *   subtracted from x.\r
4013  */\r
4014 int EventToSquare(x, limit)\r
4015      int x, limit;\r
4016 {\r
4017   if (x <= 0)\r
4018     return -2;\r
4019   if (x < lineGap)\r
4020     return -1;\r
4021   x -= lineGap;\r
4022   if ((x % (squareSize + lineGap)) >= squareSize)\r
4023     return -1;\r
4024   x /= (squareSize + lineGap);\r
4025     if (x >= limit)\r
4026     return -2;\r
4027   return x;\r
4028 }\r
4029 \r
4030 typedef struct {\r
4031   char piece;\r
4032   int command;\r
4033   char* name;\r
4034 } DropEnable;\r
4035 \r
4036 DropEnable dropEnables[] = {\r
4037   { 'P', DP_Pawn, N_("Pawn") },\r
4038   { 'N', DP_Knight, N_("Knight") },\r
4039   { 'B', DP_Bishop, N_("Bishop") },\r
4040   { 'R', DP_Rook, N_("Rook") },\r
4041   { 'Q', DP_Queen, N_("Queen") },\r
4042 };\r
4043 \r
4044 VOID\r
4045 SetupDropMenu(HMENU hmenu)\r
4046 {\r
4047   int i, count, enable;\r
4048   char *p;\r
4049   extern char white_holding[], black_holding[];\r
4050   char item[MSG_SIZ];\r
4051 \r
4052   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4053     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4054                dropEnables[i].piece);\r
4055     count = 0;\r
4056     while (p && *p++ == dropEnables[i].piece) count++;\r
4057       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4058     enable = count > 0 || !appData.testLegality\r
4059       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4060                       && !appData.icsActive);\r
4061     ModifyMenu(hmenu, dropEnables[i].command,\r
4062                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4063                dropEnables[i].command, item);\r
4064   }\r
4065 }\r
4066 \r
4067 void DragPieceBegin(int x, int y)\r
4068 {\r
4069       dragInfo.lastpos.x = boardRect.left + x;\r
4070       dragInfo.lastpos.y = boardRect.top + y;\r
4071       dragInfo.from.x = fromX;\r
4072       dragInfo.from.y = fromY;\r
4073       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4074       dragInfo.start = dragInfo.from;\r
4075       SetCapture(hwndMain);\r
4076 }\r
4077 \r
4078 void DragPieceEnd(int x, int y)\r
4079 {\r
4080     ReleaseCapture();\r
4081     dragInfo.start.x = dragInfo.start.y = -1;\r
4082     dragInfo.from = dragInfo.start;\r
4083     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4084 }\r
4085 \r
4086 void ChangeDragPiece(ChessSquare piece)\r
4087 {\r
4088     dragInfo.piece = piece;\r
4089 }\r
4090 \r
4091 /* Event handler for mouse messages */\r
4092 VOID\r
4093 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4094 {\r
4095   int x, y, menuNr;\r
4096   POINT pt;\r
4097   static int recursive = 0;\r
4098   HMENU hmenu;\r
4099   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4100 \r
4101   if (recursive) {\r
4102     if (message == WM_MBUTTONUP) {\r
4103       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4104          to the middle button: we simulate pressing the left button too!\r
4105          */\r
4106       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4107       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4108     }\r
4109     return;\r
4110   }\r
4111   recursive++;\r
4112   \r
4113   pt.x = LOWORD(lParam);\r
4114   pt.y = HIWORD(lParam);\r
4115   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4116   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4117   if (!flipView && y >= 0) {\r
4118     y = BOARD_HEIGHT - 1 - y;\r
4119   }\r
4120   if (flipView && x >= 0) {\r
4121     x = BOARD_WIDTH - 1 - x;\r
4122   }\r
4123 \r
4124   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4125 \r
4126   switch (message) {\r
4127   case WM_LBUTTONDOWN:\r
4128       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4129         ClockClick(flipClock);\r
4130       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4131         ClockClick(!flipClock);\r
4132       }\r
4133       dragInfo.start.x = dragInfo.start.y = -1;\r
4134       dragInfo.from = dragInfo.start;\r
4135     if(fromX == -1 && frozen) { // not sure where this is for\r
4136                 fromX = fromY = -1; \r
4137       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4138       break;\r
4139     }\r
4140       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4141       DrawPosition(TRUE, NULL);\r
4142     break;\r
4143 \r
4144   case WM_LBUTTONUP:\r
4145       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4146       DrawPosition(TRUE, NULL);\r
4147     break;\r
4148 \r
4149   case WM_MOUSEMOVE:\r
4150     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4151     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4152     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4153     if ((appData.animateDragging || appData.highlightDragging)\r
4154         && (wParam & MK_LBUTTON)\r
4155         && dragInfo.from.x >= 0) \r
4156     {\r
4157       BOOL full_repaint = FALSE;\r
4158 \r
4159       if (appData.animateDragging) {\r
4160         dragInfo.pos = pt;\r
4161       }\r
4162       if (appData.highlightDragging) {\r
4163         SetHighlights(fromX, fromY, x, y);\r
4164         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4165             full_repaint = TRUE;\r
4166         }\r
4167       }\r
4168       \r
4169       DrawPosition( full_repaint, NULL);\r
4170       \r
4171       dragInfo.lastpos = dragInfo.pos;\r
4172     }\r
4173     break;\r
4174 \r
4175   case WM_MOUSEWHEEL: // [DM]\r
4176     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4177        /* Mouse Wheel is being rolled forward\r
4178         * Play moves forward\r
4179         */\r
4180        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4181                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4182        /* Mouse Wheel is being rolled backward\r
4183         * Play moves backward\r
4184         */\r
4185        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4186                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4187     }\r
4188     break;\r
4189 \r
4190   case WM_MBUTTONUP:\r
4191   case WM_RBUTTONUP:\r
4192     ReleaseCapture();\r
4193     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4194     break;\r
4195  \r
4196   case WM_MBUTTONDOWN:\r
4197   case WM_RBUTTONDOWN:\r
4198     ErrorPopDown();\r
4199     ReleaseCapture();\r
4200     fromX = fromY = -1;\r
4201     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4202     dragInfo.start.x = dragInfo.start.y = -1;\r
4203     dragInfo.from = dragInfo.start;\r
4204     dragInfo.lastpos = dragInfo.pos;\r
4205     if (appData.highlightDragging) {\r
4206       ClearHighlights();\r
4207     }\r
4208     if(y == -2) {\r
4209       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4210       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4211           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4212       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4213           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4214       }\r
4215       break;\r
4216     }\r
4217     DrawPosition(TRUE, NULL);\r
4218 \r
4219     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4220     switch (menuNr) {\r
4221     case 0:\r
4222       if (message == WM_MBUTTONDOWN) {\r
4223         buttonCount = 3;  /* even if system didn't think so */\r
4224         if (wParam & MK_SHIFT) \r
4225           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4226         else\r
4227           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4228       } else { /* message == WM_RBUTTONDOWN */\r
4229         /* Just have one menu, on the right button.  Windows users don't\r
4230            think to try the middle one, and sometimes other software steals\r
4231            it, or it doesn't really exist. */\r
4232         if(gameInfo.variant != VariantShogi)\r
4233             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4234         else\r
4235             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4236       }\r
4237       break;\r
4238     case 2:\r
4239       SetCapture(hwndMain);
4240       break;\r
4241     case 1:\r
4242       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4243       SetupDropMenu(hmenu);\r
4244       MenuPopup(hwnd, pt, hmenu, -1);\r
4245     default:\r
4246       break;\r
4247     }\r
4248     break;\r
4249   }\r
4250 \r
4251   recursive--;\r
4252 }\r
4253 \r
4254 /* Preprocess messages for buttons in main window */\r
4255 LRESULT CALLBACK\r
4256 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4257 {\r
4258   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4259   int i, dir;\r
4260 \r
4261   for (i=0; i<N_BUTTONS; i++) {\r
4262     if (buttonDesc[i].id == id) break;\r
4263   }\r
4264   if (i == N_BUTTONS) return 0;\r
4265   switch (message) {\r
4266   case WM_KEYDOWN:\r
4267     switch (wParam) {\r
4268     case VK_LEFT:\r
4269     case VK_RIGHT:\r
4270       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4271       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4272       return TRUE;\r
4273     }\r
4274     break;\r
4275   case WM_CHAR:\r
4276     switch (wParam) {\r
4277     case '\r':\r
4278       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4279       return TRUE;\r
4280     default:\r
4281       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4282         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4283         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4284         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4285         SetFocus(h);\r
4286         SendMessage(h, WM_CHAR, wParam, lParam);\r
4287         return TRUE;\r
4288       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4289         TypeInEvent((char)wParam);\r
4290       }\r
4291       break;\r
4292     }\r
4293     break;\r
4294   }\r
4295   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4296 }\r
4297 \r
4298 /* Process messages for Promotion dialog box */\r
4299 LRESULT CALLBACK\r
4300 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4301 {\r
4302   char promoChar;\r
4303 \r
4304   switch (message) {\r
4305   case WM_INITDIALOG: /* message: initialize dialog box */\r
4306     /* Center the dialog over the application window */\r
4307     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4308     Translate(hDlg, DLG_PromotionKing);\r
4309     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4310       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4311        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4312        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4313                SW_SHOW : SW_HIDE);\r
4314     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4315     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4316        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4317          PieceToChar(WhiteAngel) != '~') ||\r
4318         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4319          PieceToChar(BlackAngel) != '~')   ) ?\r
4320                SW_SHOW : SW_HIDE);\r
4321     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4322        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4323          PieceToChar(WhiteMarshall) != '~') ||\r
4324         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4325          PieceToChar(BlackMarshall) != '~')   ) ?\r
4326                SW_SHOW : SW_HIDE);\r
4327     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4328     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4329        gameInfo.variant != VariantShogi ?\r
4330                SW_SHOW : SW_HIDE);\r
4331     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4332        gameInfo.variant != VariantShogi ?\r
4333                SW_SHOW : SW_HIDE);\r
4334     if(gameInfo.variant == VariantShogi) {\r
4335         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4336         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4337         SetWindowText(hDlg, "Promote?");\r
4338     }\r
4339     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4340        gameInfo.variant == VariantSuper ?\r
4341                SW_SHOW : SW_HIDE);\r
4342     return TRUE;\r
4343 \r
4344   case WM_COMMAND: /* message: received a command */\r
4345     switch (LOWORD(wParam)) {\r
4346     case IDCANCEL:\r
4347       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4348       ClearHighlights();\r
4349       DrawPosition(FALSE, NULL);\r
4350       return TRUE;\r
4351     case PB_King:\r
4352       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4353       break;\r
4354     case PB_Queen:\r
4355       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4356       break;\r
4357     case PB_Rook:\r
4358       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4359       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4360       break;\r
4361     case PB_Bishop:\r
4362       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4363       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4364       break;\r
4365     case PB_Chancellor:\r
4366       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4367       break;\r
4368     case PB_Archbishop:\r
4369       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4370       break;\r
4371     case PB_Knight:\r
4372       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4373       break;\r
4374     default:\r
4375       return FALSE;\r
4376     }\r
4377     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4378     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4379     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4380     fromX = fromY = -1;\r
4381     if (!appData.highlightLastMove) {\r
4382       ClearHighlights();\r
4383       DrawPosition(FALSE, NULL);\r
4384     }\r
4385     return TRUE;\r
4386   }\r
4387   return FALSE;\r
4388 }\r
4389 \r
4390 /* Pop up promotion dialog */\r
4391 VOID\r
4392 PromotionPopup(HWND hwnd)\r
4393 {\r
4394   FARPROC lpProc;\r
4395 \r
4396   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4397   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4398     hwnd, (DLGPROC)lpProc);\r
4399   FreeProcInstance(lpProc);\r
4400 }\r
4401 \r
4402 void\r
4403 PromotionPopUp()\r
4404 {\r
4405   DrawPosition(TRUE, NULL);\r
4406   PromotionPopup(hwndMain);\r
4407 }\r
4408 \r
4409 /* Toggle ShowThinking */\r
4410 VOID\r
4411 ToggleShowThinking()\r
4412 {\r
4413   appData.showThinking = !appData.showThinking;\r
4414   ShowThinkingEvent();\r
4415 }\r
4416 \r
4417 VOID\r
4418 LoadGameDialog(HWND hwnd, char* title)\r
4419 {\r
4420   UINT number = 0;\r
4421   FILE *f;\r
4422   char fileTitle[MSG_SIZ];\r
4423   f = OpenFileDialog(hwnd, "rb", "",\r
4424                      appData.oldSaveStyle ? "gam" : "pgn",\r
4425                      GAME_FILT,\r
4426                      title, &number, fileTitle, NULL);\r
4427   if (f != NULL) {\r
4428     cmailMsgLoaded = FALSE;\r
4429     if (number == 0) {\r
4430       int error = GameListBuild(f);\r
4431       if (error) {\r
4432         DisplayError(_("Cannot build game list"), error);\r
4433       } else if (!ListEmpty(&gameList) &&\r
4434                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4435         GameListPopUp(f, fileTitle);\r
4436         return;\r
4437       }\r
4438       GameListDestroy();\r
4439       number = 1;\r
4440     }\r
4441     LoadGame(f, number, fileTitle, FALSE);\r
4442   }\r
4443 }\r
4444 \r
4445 int get_term_width()\r
4446 {\r
4447     HDC hdc;\r
4448     TEXTMETRIC tm;\r
4449     RECT rc;\r
4450     HFONT hfont, hold_font;\r
4451     LOGFONT lf;\r
4452     HWND hText;\r
4453 \r
4454     if (hwndConsole)\r
4455         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4456     else\r
4457         return 79;\r
4458 \r
4459     // get the text metrics\r
4460     hdc = GetDC(hText);\r
4461     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4462     if (consoleCF.dwEffects & CFE_BOLD)\r
4463         lf.lfWeight = FW_BOLD;\r
4464     if (consoleCF.dwEffects & CFE_ITALIC)\r
4465         lf.lfItalic = TRUE;\r
4466     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4467         lf.lfStrikeOut = TRUE;\r
4468     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4469         lf.lfUnderline = TRUE;\r
4470     hfont = CreateFontIndirect(&lf);\r
4471     hold_font = SelectObject(hdc, hfont);\r
4472     GetTextMetrics(hdc, &tm);\r
4473     SelectObject(hdc, hold_font);\r
4474     DeleteObject(hfont);\r
4475     ReleaseDC(hText, hdc);\r
4476 \r
4477     // get the rectangle\r
4478     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4479 \r
4480     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4481 }\r
4482 \r
4483 void UpdateICSWidth(HWND hText)\r
4484 {\r
4485     LONG old_width, new_width;\r
4486 \r
4487     new_width = get_term_width(hText, FALSE);\r
4488     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4489     if (new_width != old_width)\r
4490     {\r
4491         ics_update_width(new_width);\r
4492         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4493     }\r
4494 }\r
4495 \r
4496 VOID\r
4497 ChangedConsoleFont()\r
4498 {\r
4499   CHARFORMAT cfmt;\r
4500   CHARRANGE tmpsel, sel;\r
4501   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4502   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4503   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4504   PARAFORMAT paraf;\r
4505 \r
4506   cfmt.cbSize = sizeof(CHARFORMAT);\r
4507   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4508     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4509                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4510   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4511    * size.  This was undocumented in the version of MSVC++ that I had\r
4512    * when I wrote the code, but is apparently documented now.\r
4513    */\r
4514   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4515   cfmt.bCharSet = f->lf.lfCharSet;\r
4516   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4517   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4518   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4519   /* Why are the following seemingly needed too? */\r
4520   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4521   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4522   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4523   tmpsel.cpMin = 0;\r
4524   tmpsel.cpMax = -1; /*999999?*/\r
4525   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4526   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4527   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4528    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4529    */\r
4530   paraf.cbSize = sizeof(paraf);\r
4531   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4532   paraf.dxStartIndent = 0;\r
4533   paraf.dxOffset = WRAP_INDENT;\r
4534   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4535   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4536   UpdateICSWidth(hText);\r
4537 }\r
4538 \r
4539 /*---------------------------------------------------------------------------*\\r
4540  *\r
4541  * Window Proc for main window\r
4542  *\r
4543 \*---------------------------------------------------------------------------*/\r
4544 \r
4545 /* Process messages for main window, etc. */\r
4546 LRESULT CALLBACK\r
4547 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4548 {\r
4549   FARPROC lpProc;\r
4550   int wmId, wmEvent;\r
4551   char *defName;\r
4552   FILE *f;\r
4553   UINT number;\r
4554   char fileTitle[MSG_SIZ];\r
4555   char buf[MSG_SIZ];\r
4556   static SnapData sd;\r
4557 \r
4558   switch (message) {\r
4559 \r
4560   case WM_PAINT: /* message: repaint portion of window */\r
4561     PaintProc(hwnd);\r
4562     break;\r
4563 \r
4564   case WM_ERASEBKGND:\r
4565     if (IsIconic(hwnd)) {\r
4566       /* Cheat; change the message */\r
4567       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4568     } else {\r
4569       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4570     }\r
4571     break;\r
4572 \r
4573   case WM_LBUTTONDOWN:\r
4574   case WM_MBUTTONDOWN:\r
4575   case WM_RBUTTONDOWN:\r
4576   case WM_LBUTTONUP:\r
4577   case WM_MBUTTONUP:\r
4578   case WM_RBUTTONUP:\r
4579   case WM_MOUSEMOVE:\r
4580   case WM_MOUSEWHEEL:\r
4581     MouseEvent(hwnd, message, wParam, lParam);\r
4582     break;\r
4583 \r
4584   JAWS_KB_NAVIGATION\r
4585 \r
4586   case WM_CHAR:\r
4587     \r
4588     JAWS_ALT_INTERCEPT\r
4589 \r
4590     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4591         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4592         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4593         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4594         SetFocus(h);\r
4595         SendMessage(h, message, wParam, lParam);\r
4596     } else if(lParam != KF_REPEAT) {\r
4597         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4598                 TypeInEvent((char)wParam);\r
4599         } else if((char)wParam == 003) CopyGameToClipboard();\r
4600          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4601     }\r
4602 \r
4603     break;\r
4604 \r
4605   case WM_PALETTECHANGED:\r
4606     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4607       int nnew;\r
4608       HDC hdc = GetDC(hwndMain);\r
4609       SelectPalette(hdc, hPal, TRUE);\r
4610       nnew = RealizePalette(hdc);\r
4611       if (nnew > 0) {\r
4612         paletteChanged = TRUE;\r
4613         InvalidateRect(hwnd, &boardRect, FALSE);\r
4614       }\r
4615       ReleaseDC(hwnd, hdc);\r
4616     }\r
4617     break;\r
4618 \r
4619   case WM_QUERYNEWPALETTE:\r
4620     if (!appData.monoMode /*&& paletteChanged*/) {\r
4621       int nnew;\r
4622       HDC hdc = GetDC(hwndMain);\r
4623       paletteChanged = FALSE;\r
4624       SelectPalette(hdc, hPal, FALSE);\r
4625       nnew = RealizePalette(hdc);\r
4626       if (nnew > 0) {\r
4627         InvalidateRect(hwnd, &boardRect, FALSE);\r
4628       }\r
4629       ReleaseDC(hwnd, hdc);\r
4630       return TRUE;\r
4631     }\r
4632     return FALSE;\r
4633 \r
4634   case WM_COMMAND: /* message: command from application menu */\r
4635     wmId    = LOWORD(wParam);\r
4636     wmEvent = HIWORD(wParam);\r
4637 \r
4638     switch (wmId) {\r
4639     case IDM_NewGame:\r
4640       ResetGameEvent();\r
4641       SAY("new game enter a move to play against the computer with white");\r
4642       break;\r
4643 \r
4644     case IDM_NewGameFRC:\r
4645       if( NewGameFRC() == 0 ) {\r
4646         ResetGameEvent();\r
4647       }\r
4648       break;\r
4649 \r
4650     case IDM_NewVariant:\r
4651       NewVariantPopup(hwnd);\r
4652       break;\r
4653 \r
4654     case IDM_LoadGame:\r
4655       LoadGameDialog(hwnd, _("Load Game from File"));\r
4656       break;\r
4657 \r
4658     case IDM_LoadNextGame:\r
4659       ReloadGame(1);\r
4660       break;\r
4661 \r
4662     case IDM_LoadPrevGame:\r
4663       ReloadGame(-1);\r
4664       break;\r
4665 \r
4666     case IDM_ReloadGame:\r
4667       ReloadGame(0);\r
4668       break;\r
4669 \r
4670     case IDM_LoadPosition:\r
4671       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4672         Reset(FALSE, TRUE);\r
4673       }\r
4674       number = 1;\r
4675       f = OpenFileDialog(hwnd, "rb", "",\r
4676                          appData.oldSaveStyle ? "pos" : "fen",\r
4677                          POSITION_FILT,\r
4678                          _("Load Position from File"), &number, fileTitle, NULL);\r
4679       if (f != NULL) {\r
4680         LoadPosition(f, number, fileTitle);\r
4681       }\r
4682       break;\r
4683 \r
4684     case IDM_LoadNextPosition:\r
4685       ReloadPosition(1);\r
4686       break;\r
4687 \r
4688     case IDM_LoadPrevPosition:\r
4689       ReloadPosition(-1);\r
4690       break;\r
4691 \r
4692     case IDM_ReloadPosition:\r
4693       ReloadPosition(0);\r
4694       break;\r
4695 \r
4696     case IDM_SaveGame:\r
4697       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4698       f = OpenFileDialog(hwnd, "a", defName,\r
4699                          appData.oldSaveStyle ? "gam" : "pgn",\r
4700                          GAME_FILT,\r
4701                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4702       if (f != NULL) {\r
4703         SaveGame(f, 0, "");\r
4704       }\r
4705       break;\r
4706 \r
4707     case IDM_SavePosition:\r
4708       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4709       f = OpenFileDialog(hwnd, "a", defName,\r
4710                          appData.oldSaveStyle ? "pos" : "fen",\r
4711                          POSITION_FILT,\r
4712                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4713       if (f != NULL) {\r
4714         SavePosition(f, 0, "");\r
4715       }\r
4716       break;\r
4717 \r
4718     case IDM_SaveDiagram:\r
4719       defName = "diagram";\r
4720       f = OpenFileDialog(hwnd, "wb", defName,\r
4721                          "bmp",\r
4722                          DIAGRAM_FILT,\r
4723                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4724       if (f != NULL) {\r
4725         SaveDiagram(f);\r
4726       }\r
4727       break;\r
4728 \r
4729     case IDM_CopyGame:\r
4730       CopyGameToClipboard();\r
4731       break;\r
4732 \r
4733     case IDM_PasteGame:\r
4734       PasteGameFromClipboard();\r
4735       break;\r
4736 \r
4737     case IDM_CopyGameListToClipboard:\r
4738       CopyGameListToClipboard();\r
4739       break;\r
4740 \r
4741     /* [AS] Autodetect FEN or PGN data */\r
4742     case IDM_PasteAny:\r
4743       PasteGameOrFENFromClipboard();\r
4744       break;\r
4745 \r
4746     /* [AS] Move history */\r
4747     case IDM_ShowMoveHistory:\r
4748         if( MoveHistoryIsUp() ) {\r
4749             MoveHistoryPopDown();\r
4750         }\r
4751         else {\r
4752             MoveHistoryPopUp();\r
4753         }\r
4754         break;\r
4755 \r
4756     /* [AS] Eval graph */\r
4757     case IDM_ShowEvalGraph:\r
4758         if( EvalGraphIsUp() ) {\r
4759             EvalGraphPopDown();\r
4760         }\r
4761         else {\r
4762             EvalGraphPopUp();\r
4763             SetFocus(hwndMain);\r
4764         }\r
4765         break;\r
4766 \r
4767     /* [AS] Engine output */\r
4768     case IDM_ShowEngineOutput:\r
4769         if( EngineOutputIsUp() ) {\r
4770             EngineOutputPopDown();\r
4771         }\r
4772         else {\r
4773             EngineOutputPopUp();\r
4774         }\r
4775         break;\r
4776 \r
4777     /* [AS] User adjudication */\r
4778     case IDM_UserAdjudication_White:\r
4779         UserAdjudicationEvent( +1 );\r
4780         break;\r
4781 \r
4782     case IDM_UserAdjudication_Black:\r
4783         UserAdjudicationEvent( -1 );\r
4784         break;\r
4785 \r
4786     case IDM_UserAdjudication_Draw:\r
4787         UserAdjudicationEvent( 0 );\r
4788         break;\r
4789 \r
4790     /* [AS] Game list options dialog */\r
4791     case IDM_GameListOptions:\r
4792       GameListOptions();\r
4793       break;\r
4794 \r
4795     case IDM_NewChat:\r
4796       ChatPopUp(NULL);\r
4797       break;\r
4798 \r
4799     case IDM_CopyPosition:\r
4800       CopyFENToClipboard();\r
4801       break;\r
4802 \r
4803     case IDM_PastePosition:\r
4804       PasteFENFromClipboard();\r
4805       break;\r
4806 \r
4807     case IDM_MailMove:\r
4808       MailMoveEvent();\r
4809       break;\r
4810 \r
4811     case IDM_ReloadCMailMsg:\r
4812       Reset(TRUE, TRUE);\r
4813       ReloadCmailMsgEvent(FALSE);\r
4814       break;\r
4815 \r
4816     case IDM_Minimize:\r
4817       ShowWindow(hwnd, SW_MINIMIZE);\r
4818       break;\r
4819 \r
4820     case IDM_Exit:\r
4821       ExitEvent(0);\r
4822       break;\r
4823 \r
4824     case IDM_MachineWhite:\r
4825       MachineWhiteEvent();\r
4826       /*\r
4827        * refresh the tags dialog only if it's visible\r
4828        */\r
4829       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4830           char *tags;\r
4831           tags = PGNTags(&gameInfo);\r
4832           TagsPopUp(tags, CmailMsg());\r
4833           free(tags);\r
4834       }\r
4835       SAY("computer starts playing white");\r
4836       break;\r
4837 \r
4838     case IDM_MachineBlack:\r
4839       MachineBlackEvent();\r
4840       /*\r
4841        * refresh the tags dialog only if it's visible\r
4842        */\r
4843       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4844           char *tags;\r
4845           tags = PGNTags(&gameInfo);\r
4846           TagsPopUp(tags, CmailMsg());\r
4847           free(tags);\r
4848       }\r
4849       SAY("computer starts playing black");\r
4850       break;\r
4851 \r
4852     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4853       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4854       break;\r
4855 \r
4856     case IDM_TwoMachines:\r
4857       TwoMachinesEvent();\r
4858       /*\r
4859        * refresh the tags dialog only if it's visible\r
4860        */\r
4861       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4862           char *tags;\r
4863           tags = PGNTags(&gameInfo);\r
4864           TagsPopUp(tags, CmailMsg());\r
4865           free(tags);\r
4866       }\r
4867       SAY("computer starts playing both sides");\r
4868       break;\r
4869 \r
4870     case IDM_AnalysisMode:\r
4871       if (!first.analysisSupport) {\r
4872         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4873         DisplayError(buf, 0);\r
4874       } else {\r
4875         SAY("analyzing current position");\r
4876         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4877         if (appData.icsActive) {\r
4878                if (gameMode != IcsObserving) {\r
4879                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4880                        DisplayError(buf, 0);\r
4881                        /* secure check */\r
4882                        if (appData.icsEngineAnalyze) {\r
4883                                if (appData.debugMode) \r
4884                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4885                                ExitAnalyzeMode();\r
4886                                ModeHighlight();\r
4887                                break;\r
4888                        }\r
4889                        break;\r
4890                } else {\r
4891                        /* if enable, user want disable icsEngineAnalyze */\r
4892                        if (appData.icsEngineAnalyze) {\r
4893                                ExitAnalyzeMode();\r
4894                                ModeHighlight();\r
4895                                break;\r
4896                        }\r
4897                        appData.icsEngineAnalyze = TRUE;\r
4898                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4899                }\r
4900         } \r
4901         if (!appData.showThinking) ToggleShowThinking();\r
4902         AnalyzeModeEvent();\r
4903       }\r
4904       break;\r
4905 \r
4906     case IDM_AnalyzeFile:\r
4907       if (!first.analysisSupport) {\r
4908         char buf[MSG_SIZ];\r
4909           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4910         DisplayError(buf, 0);\r
4911       } else {\r
4912         if (!appData.showThinking) ToggleShowThinking();\r
4913         AnalyzeFileEvent();\r
4914         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4915         AnalysisPeriodicEvent(1);\r
4916       }\r
4917       break;\r
4918 \r
4919     case IDM_IcsClient:\r
4920       IcsClientEvent();\r
4921       break;\r
4922 \r
4923     case IDM_EditGame:\r
4924     case IDM_EditGame2:\r
4925       EditGameEvent();\r
4926       SAY("edit game");\r
4927       break;\r
4928 \r
4929     case IDM_EditPosition:\r
4930     case IDM_EditPosition2:\r
4931       EditPositionEvent();\r
4932       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4933       break;\r
4934 \r
4935     case IDM_Training:\r
4936       TrainingEvent();\r
4937       break;\r
4938 \r
4939     case IDM_ShowGameList:\r
4940       ShowGameListProc();\r
4941       break;\r
4942 \r
4943     case IDM_EditProgs1:\r
4944       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4945       break;\r
4946 \r
4947     case IDM_EditProgs2:\r
4948      LoadEnginePopUp(hwndMain);\r
4949 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4950       break;\r
4951 \r
4952     case IDM_EditServers:\r
4953       EditTagsPopUp(icsNames, &icsNames);\r
4954       break;\r
4955 \r
4956     case IDM_EditTags:\r
4957     case IDM_Tags:\r
4958       EditTagsProc();\r
4959       break;\r
4960 \r
4961     case IDM_EditBook:\r
4962       EditBookEvent();\r
4963       break;\r
4964 \r
4965     case IDM_EditComment:\r
4966     case IDM_Comment:\r
4967       if (commentUp && editComment) {\r
4968         CommentPopDown();\r
4969       } else {\r
4970         EditCommentEvent();\r
4971       }\r
4972       break;\r
4973 \r
4974     case IDM_Pause:\r
4975       PauseEvent();\r
4976       break;\r
4977 \r
4978     case IDM_Accept:\r
4979       AcceptEvent();\r
4980       break;\r
4981 \r
4982     case IDM_Decline:\r
4983       DeclineEvent();\r
4984       break;\r
4985 \r
4986     case IDM_Rematch:\r
4987       RematchEvent();\r
4988       break;\r
4989 \r
4990     case IDM_CallFlag:\r
4991       CallFlagEvent();\r
4992       break;\r
4993 \r
4994     case IDM_Draw:\r
4995       DrawEvent();\r
4996       break;\r
4997 \r
4998     case IDM_Adjourn:\r
4999       AdjournEvent();\r
5000       break;\r
5001 \r
5002     case IDM_Abort:\r
5003       AbortEvent();\r
5004       break;\r
5005 \r
5006     case IDM_Resign:\r
5007       ResignEvent();\r
5008       break;\r
5009 \r
5010     case IDM_StopObserving:\r
5011       StopObservingEvent();\r
5012       break;\r
5013 \r
5014     case IDM_StopExamining:\r
5015       StopExaminingEvent();\r
5016       break;\r
5017 \r
5018     case IDM_Upload:\r
5019       UploadGameEvent();\r
5020       break;\r
5021 \r
5022     case IDM_TypeInMove:\r
5023       TypeInEvent('\000');\r
5024       break;\r
5025 \r
5026     case IDM_TypeInName:\r
5027       PopUpNameDialog('\000');\r
5028       break;\r
5029 \r
5030     case IDM_Backward:\r
5031       BackwardEvent();\r
5032       SetFocus(hwndMain);\r
5033       break;\r
5034 \r
5035     JAWS_MENU_ITEMS\r
5036 \r
5037     case IDM_Forward:\r
5038       ForwardEvent();\r
5039       SetFocus(hwndMain);\r
5040       break;\r
5041 \r
5042     case IDM_ToStart:\r
5043       ToStartEvent();\r
5044       SetFocus(hwndMain);\r
5045       break;\r
5046 \r
5047     case IDM_ToEnd:\r
5048       ToEndEvent();\r
5049       SetFocus(hwndMain);\r
5050       break;\r
5051 \r
5052     case IDM_Revert:\r
5053       RevertEvent(FALSE);\r
5054       break;\r
5055 \r
5056     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5057       RevertEvent(TRUE);\r
5058       break;\r
5059 \r
5060     case IDM_TruncateGame:\r
5061       TruncateGameEvent();\r
5062       break;\r
5063 \r
5064     case IDM_MoveNow:\r
5065       MoveNowEvent();\r
5066       break;\r
5067 \r
5068     case IDM_RetractMove:\r
5069       RetractMoveEvent();\r
5070       break;\r
5071 \r
5072     case IDM_FlipView:\r
5073       flipView = !flipView;\r
5074       DrawPosition(FALSE, NULL);\r
5075       break;\r
5076 \r
5077     case IDM_FlipClock:\r
5078       flipClock = !flipClock;\r
5079       DisplayBothClocks();\r
5080       DisplayLogos();\r
5081       break;\r
5082 \r
5083     case IDM_MuteSounds:\r
5084       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5085       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5086                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5087       break;\r
5088 \r
5089     case IDM_GeneralOptions:\r
5090       GeneralOptionsPopup(hwnd);\r
5091       DrawPosition(TRUE, NULL);\r
5092       break;\r
5093 \r
5094     case IDM_BoardOptions:\r
5095       BoardOptionsPopup(hwnd);\r
5096       break;\r
5097 \r
5098     case IDM_EnginePlayOptions:\r
5099       EnginePlayOptionsPopup(hwnd);\r
5100       break;\r
5101 \r
5102     case IDM_Engine1Options:\r
5103       EngineOptionsPopup(hwnd, &first);\r
5104       break;\r
5105 \r
5106     case IDM_Engine2Options:\r
5107       savedHwnd = hwnd;\r
5108       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5109       EngineOptionsPopup(hwnd, &second);\r
5110       break;\r
5111 \r
5112     case IDM_OptionsUCI:\r
5113       UciOptionsPopup(hwnd);\r
5114       break;\r
5115 \r
5116     case IDM_Tourney:\r
5117       TourneyPopup(hwnd);\r
5118       break;\r
5119 \r
5120     case IDM_IcsOptions:\r
5121       IcsOptionsPopup(hwnd);\r
5122       break;\r
5123 \r
5124     case IDM_Fonts:\r
5125       FontsOptionsPopup(hwnd);\r
5126       break;\r
5127 \r
5128     case IDM_Sounds:\r
5129       SoundOptionsPopup(hwnd);\r
5130       break;\r
5131 \r
5132     case IDM_CommPort:\r
5133       CommPortOptionsPopup(hwnd);\r
5134       break;\r
5135 \r
5136     case IDM_LoadOptions:\r
5137       LoadOptionsPopup(hwnd);\r
5138       break;\r
5139 \r
5140     case IDM_SaveOptions:\r
5141       SaveOptionsPopup(hwnd);\r
5142       break;\r
5143 \r
5144     case IDM_TimeControl:\r
5145       TimeControlOptionsPopup(hwnd);\r
5146       break;\r
5147 \r
5148     case IDM_SaveSettings:\r
5149       SaveSettings(settingsFileName);\r
5150       break;\r
5151 \r
5152     case IDM_SaveSettingsOnExit:\r
5153       saveSettingsOnExit = !saveSettingsOnExit;\r
5154       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5155                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5156                                          MF_CHECKED : MF_UNCHECKED));\r
5157       break;\r
5158 \r
5159     case IDM_Hint:\r
5160       HintEvent();\r
5161       break;\r
5162 \r
5163     case IDM_Book:\r
5164       BookEvent();\r
5165       break;\r
5166 \r
5167     case IDM_AboutGame:\r
5168       AboutGameEvent();\r
5169       break;\r
5170 \r
5171     case IDM_Debug:\r
5172       appData.debugMode = !appData.debugMode;\r
5173       if (appData.debugMode) {\r
5174         char dir[MSG_SIZ];\r
5175         GetCurrentDirectory(MSG_SIZ, dir);\r
5176         SetCurrentDirectory(installDir);\r
5177         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5178         SetCurrentDirectory(dir);\r
5179         setbuf(debugFP, NULL);\r
5180       } else {\r
5181         fclose(debugFP);\r
5182         debugFP = NULL;\r
5183       }\r
5184       break;\r
5185 \r
5186     case IDM_HELPCONTENTS:\r
5187       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5188           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5189           MessageBox (GetFocus(),\r
5190                     _("Unable to activate help"),\r
5191                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5192       }\r
5193       break;\r
5194 \r
5195     case IDM_HELPSEARCH:\r
5196         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5197             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5198         MessageBox (GetFocus(),\r
5199                     _("Unable to activate help"),\r
5200                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5201       }\r
5202       break;\r
5203 \r
5204     case IDM_HELPHELP:\r
5205       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5206         MessageBox (GetFocus(),\r
5207                     _("Unable to activate help"),\r
5208                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5209       }\r
5210       break;\r
5211 \r
5212     case IDM_ABOUT:\r
5213       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5214       DialogBox(hInst, \r
5215         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5216         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5217       FreeProcInstance(lpProc);\r
5218       break;\r
5219 \r
5220     case IDM_DirectCommand1:\r
5221       AskQuestionEvent(_("Direct Command"),\r
5222                        _("Send to chess program:"), "", "1");\r
5223       break;\r
5224     case IDM_DirectCommand2:\r
5225       AskQuestionEvent(_("Direct Command"),\r
5226                        _("Send to second chess program:"), "", "2");\r
5227       break;\r
5228 \r
5229     case EP_WhitePawn:\r
5230       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5231       fromX = fromY = -1;\r
5232       break;\r
5233 \r
5234     case EP_WhiteKnight:\r
5235       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5236       fromX = fromY = -1;\r
5237       break;\r
5238 \r
5239     case EP_WhiteBishop:\r
5240       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5241       fromX = fromY = -1;\r
5242       break;\r
5243 \r
5244     case EP_WhiteRook:\r
5245       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5246       fromX = fromY = -1;\r
5247       break;\r
5248 \r
5249     case EP_WhiteQueen:\r
5250       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5251       fromX = fromY = -1;\r
5252       break;\r
5253 \r
5254     case EP_WhiteFerz:\r
5255       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5256       fromX = fromY = -1;\r
5257       break;\r
5258 \r
5259     case EP_WhiteWazir:\r
5260       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5261       fromX = fromY = -1;\r
5262       break;\r
5263 \r
5264     case EP_WhiteAlfil:\r
5265       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5266       fromX = fromY = -1;\r
5267       break;\r
5268 \r
5269     case EP_WhiteCannon:\r
5270       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5271       fromX = fromY = -1;\r
5272       break;\r
5273 \r
5274     case EP_WhiteCardinal:\r
5275       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5276       fromX = fromY = -1;\r
5277       break;\r
5278 \r
5279     case EP_WhiteMarshall:\r
5280       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5281       fromX = fromY = -1;\r
5282       break;\r
5283 \r
5284     case EP_WhiteKing:\r
5285       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5286       fromX = fromY = -1;\r
5287       break;\r
5288 \r
5289     case EP_BlackPawn:\r
5290       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5291       fromX = fromY = -1;\r
5292       break;\r
5293 \r
5294     case EP_BlackKnight:\r
5295       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5296       fromX = fromY = -1;\r
5297       break;\r
5298 \r
5299     case EP_BlackBishop:\r
5300       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5301       fromX = fromY = -1;\r
5302       break;\r
5303 \r
5304     case EP_BlackRook:\r
5305       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5306       fromX = fromY = -1;\r
5307       break;\r
5308 \r
5309     case EP_BlackQueen:\r
5310       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5311       fromX = fromY = -1;\r
5312       break;\r
5313 \r
5314     case EP_BlackFerz:\r
5315       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5316       fromX = fromY = -1;\r
5317       break;\r
5318 \r
5319     case EP_BlackWazir:\r
5320       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5321       fromX = fromY = -1;\r
5322       break;\r
5323 \r
5324     case EP_BlackAlfil:\r
5325       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5326       fromX = fromY = -1;\r
5327       break;\r
5328 \r
5329     case EP_BlackCannon:\r
5330       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5331       fromX = fromY = -1;\r
5332       break;\r
5333 \r
5334     case EP_BlackCardinal:\r
5335       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5336       fromX = fromY = -1;\r
5337       break;\r
5338 \r
5339     case EP_BlackMarshall:\r
5340       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5341       fromX = fromY = -1;\r
5342       break;\r
5343 \r
5344     case EP_BlackKing:\r
5345       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5346       fromX = fromY = -1;\r
5347       break;\r
5348 \r
5349     case EP_EmptySquare:\r
5350       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5351       fromX = fromY = -1;\r
5352       break;\r
5353 \r
5354     case EP_ClearBoard:\r
5355       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5356       fromX = fromY = -1;\r
5357       break;\r
5358 \r
5359     case EP_White:\r
5360       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5361       fromX = fromY = -1;\r
5362       break;\r
5363 \r
5364     case EP_Black:\r
5365       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5366       fromX = fromY = -1;\r
5367       break;\r
5368 \r
5369     case EP_Promote:\r
5370       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5371       fromX = fromY = -1;\r
5372       break;\r
5373 \r
5374     case EP_Demote:\r
5375       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5376       fromX = fromY = -1;\r
5377       break;\r
5378 \r
5379     case DP_Pawn:\r
5380       DropMenuEvent(WhitePawn, fromX, fromY);\r
5381       fromX = fromY = -1;\r
5382       break;\r
5383 \r
5384     case DP_Knight:\r
5385       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5386       fromX = fromY = -1;\r
5387       break;\r
5388 \r
5389     case DP_Bishop:\r
5390       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5391       fromX = fromY = -1;\r
5392       break;\r
5393 \r
5394     case DP_Rook:\r
5395       DropMenuEvent(WhiteRook, fromX, fromY);\r
5396       fromX = fromY = -1;\r
5397       break;\r
5398 \r
5399     case DP_Queen:\r
5400       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5401       fromX = fromY = -1;\r
5402       break;\r
5403 \r
5404     case IDM_English:\r
5405       barbaric = 0; appData.language = "";\r
5406       TranslateMenus(0);\r
5407       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5408       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5409       lastChecked = wmId;\r
5410       break;\r
5411 \r
5412     default:\r
5413       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5414           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5415           TranslateMenus(0);\r
5416           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5417           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5418           lastChecked = wmId;\r
5419           break;\r
5420       }\r
5421       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5422     }\r
5423     break;\r
5424 \r
5425   case WM_TIMER:\r
5426     switch (wParam) {\r
5427     case CLOCK_TIMER_ID:\r
5428       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5429       clockTimerEvent = 0;\r
5430       DecrementClocks(); /* call into back end */\r
5431       break;\r
5432     case LOAD_GAME_TIMER_ID:\r
5433       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5434       loadGameTimerEvent = 0;\r
5435       AutoPlayGameLoop(); /* call into back end */\r
5436       break;\r
5437     case ANALYSIS_TIMER_ID:\r
5438       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5439                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5440         AnalysisPeriodicEvent(0);\r
5441       } else {\r
5442         KillTimer(hwnd, analysisTimerEvent);\r
5443         analysisTimerEvent = 0;\r
5444       }\r
5445       break;\r
5446     case DELAYED_TIMER_ID:\r
5447       KillTimer(hwnd, delayedTimerEvent);\r
5448       delayedTimerEvent = 0;\r
5449       delayedTimerCallback();\r
5450       break;\r
5451     }\r
5452     break;\r
5453 \r
5454   case WM_USER_Input:\r
5455     InputEvent(hwnd, message, wParam, lParam);\r
5456     break;\r
5457 \r
5458   /* [AS] Also move "attached" child windows */\r
5459   case WM_WINDOWPOSCHANGING:\r
5460 \r
5461     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5462         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5463 \r
5464         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5465             /* Window is moving */\r
5466             RECT rcMain;\r
5467 \r
5468 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5469             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5470             rcMain.right  = wpMain.x + wpMain.width;\r
5471             rcMain.top    = wpMain.y;\r
5472             rcMain.bottom = wpMain.y + wpMain.height;\r
5473             \r
5474             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5475             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5476             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5477             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5478             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5479             wpMain.x = lpwp->x;\r
5480             wpMain.y = lpwp->y;\r
5481         }\r
5482     }\r
5483     break;\r
5484 \r
5485   /* [AS] Snapping */\r
5486   case WM_ENTERSIZEMOVE:\r
5487     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5488     if (hwnd == hwndMain) {\r
5489       doingSizing = TRUE;\r
5490       lastSizing = 0;\r
5491     }\r
5492     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5493     break;\r
5494 \r
5495   case WM_SIZING:\r
5496     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5497     if (hwnd == hwndMain) {\r
5498       lastSizing = wParam;\r
5499     }\r
5500     break;\r
5501 \r
5502   case WM_MOVING:\r
5503     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5504       return OnMoving( &sd, hwnd, wParam, lParam );\r
5505 \r
5506   case WM_EXITSIZEMOVE:\r
5507     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5508     if (hwnd == hwndMain) {\r
5509       RECT client;\r
5510       doingSizing = FALSE;\r
5511       InvalidateRect(hwnd, &boardRect, FALSE);\r
5512       GetClientRect(hwnd, &client);\r
5513       ResizeBoard(client.right, client.bottom, lastSizing);\r
5514       lastSizing = 0;\r
5515       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5516     }\r
5517     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5518     break;\r
5519 \r
5520   case WM_DESTROY: /* message: window being destroyed */\r
5521     PostQuitMessage(0);\r
5522     break;\r
5523 \r
5524   case WM_CLOSE:\r
5525     if (hwnd == hwndMain) {\r
5526       ExitEvent(0);\r
5527     }\r
5528     break;\r
5529 \r
5530   default:      /* Passes it on if unprocessed */\r
5531     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5532   }\r
5533   return 0;\r
5534 }\r
5535 \r
5536 /*---------------------------------------------------------------------------*\\r
5537  *\r
5538  * Misc utility routines\r
5539  *\r
5540 \*---------------------------------------------------------------------------*/\r
5541 \r
5542 /*\r
5543  * Decent random number generator, at least not as bad as Windows\r
5544  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5545  */\r
5546 unsigned int randstate;\r
5547 \r
5548 int\r
5549 myrandom(void)\r
5550 {\r
5551   randstate = randstate * 1664525 + 1013904223;\r
5552   return (int) randstate & 0x7fffffff;\r
5553 }\r
5554 \r
5555 void\r
5556 mysrandom(unsigned int seed)\r
5557 {\r
5558   randstate = seed;\r
5559 }\r
5560 \r
5561 \r
5562 /* \r
5563  * returns TRUE if user selects a different color, FALSE otherwise \r
5564  */\r
5565 \r
5566 BOOL\r
5567 ChangeColor(HWND hwnd, COLORREF *which)\r
5568 {\r
5569   static BOOL firstTime = TRUE;\r
5570   static DWORD customColors[16];\r
5571   CHOOSECOLOR cc;\r
5572   COLORREF newcolor;\r
5573   int i;\r
5574   ColorClass ccl;\r
5575 \r
5576   if (firstTime) {\r
5577     /* Make initial colors in use available as custom colors */\r
5578     /* Should we put the compiled-in defaults here instead? */\r
5579     i = 0;\r
5580     customColors[i++] = lightSquareColor & 0xffffff;\r
5581     customColors[i++] = darkSquareColor & 0xffffff;\r
5582     customColors[i++] = whitePieceColor & 0xffffff;\r
5583     customColors[i++] = blackPieceColor & 0xffffff;\r
5584     customColors[i++] = highlightSquareColor & 0xffffff;\r
5585     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5586 \r
5587     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5588       customColors[i++] = textAttribs[ccl].color;\r
5589     }\r
5590     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5591     firstTime = FALSE;\r
5592   }\r
5593 \r
5594   cc.lStructSize = sizeof(cc);\r
5595   cc.hwndOwner = hwnd;\r
5596   cc.hInstance = NULL;\r
5597   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5598   cc.lpCustColors = (LPDWORD) customColors;\r
5599   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5600 \r
5601   if (!ChooseColor(&cc)) return FALSE;\r
5602 \r
5603   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5604   if (newcolor == *which) return FALSE;\r
5605   *which = newcolor;\r
5606   return TRUE;\r
5607 \r
5608   /*\r
5609   InitDrawingColors();\r
5610   InvalidateRect(hwnd, &boardRect, FALSE);\r
5611   */\r
5612 }\r
5613 \r
5614 BOOLEAN\r
5615 MyLoadSound(MySound *ms)\r
5616 {\r
5617   BOOL ok = FALSE;\r
5618   struct stat st;\r
5619   FILE *f;\r
5620 \r
5621   if (ms->data) free(ms->data);\r
5622   ms->data = NULL;\r
5623 \r
5624   switch (ms->name[0]) {\r
5625   case NULLCHAR:\r
5626     /* Silence */\r
5627     ok = TRUE;\r
5628     break;\r
5629   case '$':\r
5630     /* System sound from Control Panel.  Don't preload here. */\r
5631     ok = TRUE;\r
5632     break;\r
5633   case '!':\r
5634     if (ms->name[1] == NULLCHAR) {\r
5635       /* "!" alone = silence */\r
5636       ok = TRUE;\r
5637     } else {\r
5638       /* Builtin wave resource.  Error if not found. */\r
5639       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5640       if (h == NULL) break;\r
5641       ms->data = (void *)LoadResource(hInst, h);\r
5642       if (h == NULL) break;\r
5643       ok = TRUE;\r
5644     }\r
5645     break;\r
5646   default:\r
5647     /* .wav file.  Error if not found. */\r
5648     f = fopen(ms->name, "rb");\r
5649     if (f == NULL) break;\r
5650     if (fstat(fileno(f), &st) < 0) break;\r
5651     ms->data = malloc(st.st_size);\r
5652     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5653     fclose(f);\r
5654     ok = TRUE;\r
5655     break;\r
5656   }\r
5657   if (!ok) {\r
5658     char buf[MSG_SIZ];\r
5659       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5660     DisplayError(buf, GetLastError());\r
5661   }\r
5662   return ok;\r
5663 }\r
5664 \r
5665 BOOLEAN\r
5666 MyPlaySound(MySound *ms)\r
5667 {\r
5668   BOOLEAN ok = FALSE;\r
5669 \r
5670   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5671   switch (ms->name[0]) {\r
5672   case NULLCHAR:\r
5673         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5674     /* Silence */\r
5675     ok = TRUE;\r
5676     break;\r
5677   case '$':\r
5678     /* System sound from Control Panel (deprecated feature).\r
5679        "$" alone or an unset sound name gets default beep (still in use). */\r
5680     if (ms->name[1]) {\r
5681       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5682     }\r
5683     if (!ok) ok = MessageBeep(MB_OK);\r
5684     break; \r
5685   case '!':\r
5686     /* Builtin wave resource, or "!" alone for silence */\r
5687     if (ms->name[1]) {\r
5688       if (ms->data == NULL) return FALSE;\r
5689       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5690     } else {\r
5691       ok = TRUE;\r
5692     }\r
5693     break;\r
5694   default:\r
5695     /* .wav file.  Error if not found. */\r
5696     if (ms->data == NULL) return FALSE;\r
5697     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5698     break;\r
5699   }\r
5700   /* Don't print an error: this can happen innocently if the sound driver\r
5701      is busy; for instance, if another instance of WinBoard is playing\r
5702      a sound at about the same time. */\r
5703   return ok;\r
5704 }\r
5705 \r
5706 \r
5707 LRESULT CALLBACK\r
5708 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5709 {\r
5710   BOOL ok;\r
5711   OPENFILENAME *ofn;\r
5712   static UINT *number; /* gross that this is static */\r
5713 \r
5714   switch (message) {\r
5715   case WM_INITDIALOG: /* message: initialize dialog box */\r
5716     /* Center the dialog over the application window */\r
5717     ofn = (OPENFILENAME *) lParam;\r
5718     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5719       number = (UINT *) ofn->lCustData;\r
5720       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5721     } else {\r
5722       number = NULL;\r
5723     }\r
5724     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5725     Translate(hDlg, 1536);\r
5726     return FALSE;  /* Allow for further processing */\r
5727 \r
5728   case WM_COMMAND:\r
5729     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5730       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5731     }\r
5732     return FALSE;  /* Allow for further processing */\r
5733   }\r
5734   return FALSE;\r
5735 }\r
5736 \r
5737 UINT APIENTRY\r
5738 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5739 {\r
5740   static UINT *number;\r
5741   OPENFILENAME *ofname;\r
5742   OFNOTIFY *ofnot;\r
5743   switch (uiMsg) {\r
5744   case WM_INITDIALOG:\r
5745     Translate(hdlg, DLG_IndexNumber);\r
5746     ofname = (OPENFILENAME *)lParam;\r
5747     number = (UINT *)(ofname->lCustData);\r
5748     break;\r
5749   case WM_NOTIFY:\r
5750     ofnot = (OFNOTIFY *)lParam;\r
5751     if (ofnot->hdr.code == CDN_FILEOK) {\r
5752       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5753     }\r
5754     break;\r
5755   }\r
5756   return 0;\r
5757 }\r
5758 \r
5759 \r
5760 FILE *\r
5761 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5762                char *nameFilt, char *dlgTitle, UINT *number,\r
5763                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5764 {\r
5765   OPENFILENAME openFileName;\r
5766   char buf1[MSG_SIZ];\r
5767   FILE *f;\r
5768 \r
5769   if (fileName == NULL) fileName = buf1;\r
5770   if (defName == NULL) {\r
5771     safeStrCpy(fileName, "*.", 3 );\r
5772     strcat(fileName, defExt);\r
5773   } else {\r
5774     safeStrCpy(fileName, defName, MSG_SIZ );\r
5775   }\r
5776     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5777   if (number) *number = 0;\r
5778 \r
5779   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5780   openFileName.hwndOwner         = hwnd;\r
5781   openFileName.hInstance         = (HANDLE) hInst;\r
5782   openFileName.lpstrFilter       = nameFilt;\r
5783   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5784   openFileName.nMaxCustFilter    = 0L;\r
5785   openFileName.nFilterIndex      = 1L;\r
5786   openFileName.lpstrFile         = fileName;\r
5787   openFileName.nMaxFile          = MSG_SIZ;\r
5788   openFileName.lpstrFileTitle    = fileTitle;\r
5789   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5790   openFileName.lpstrInitialDir   = NULL;\r
5791   openFileName.lpstrTitle        = dlgTitle;\r
5792   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5793     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5794     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5795     | (oldDialog ? 0 : OFN_EXPLORER);\r
5796   openFileName.nFileOffset       = 0;\r
5797   openFileName.nFileExtension    = 0;\r
5798   openFileName.lpstrDefExt       = defExt;\r
5799   openFileName.lCustData         = (LONG) number;\r
5800   openFileName.lpfnHook          = oldDialog ?\r
5801     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5802   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5803 \r
5804   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5805                         GetOpenFileName(&openFileName)) {\r
5806     /* open the file */\r
5807     f = fopen(openFileName.lpstrFile, write);\r
5808     if (f == NULL) {\r
5809       MessageBox(hwnd, _("File open failed"), NULL,\r
5810                  MB_OK|MB_ICONEXCLAMATION);\r
5811       return NULL;\r
5812     }\r
5813   } else {\r
5814     int err = CommDlgExtendedError();\r
5815     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5816     return FALSE;\r
5817   }\r
5818   return f;\r
5819 }\r
5820 \r
5821 \r
5822 \r
5823 VOID APIENTRY\r
5824 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5825 {\r
5826   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5827 \r
5828   /*\r
5829    * Get the first pop-up menu in the menu template. This is the\r
5830    * menu that TrackPopupMenu displays.\r
5831    */\r
5832   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5833   TranslateOneMenu(10, hmenuTrackPopup);\r
5834 \r
5835   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5836 \r
5837   /*\r
5838    * TrackPopup uses screen coordinates, so convert the\r
5839    * coordinates of the mouse click to screen coordinates.\r
5840    */\r
5841   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5842 \r
5843   /* Draw and track the floating pop-up menu. */\r
5844   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5845                  pt.x, pt.y, 0, hwnd, NULL);\r
5846 \r
5847   /* Destroy the menu.*/\r
5848   DestroyMenu(hmenu);\r
5849 }\r
5850    \r
5851 typedef struct {\r
5852   HWND hDlg, hText;\r
5853   int sizeX, sizeY, newSizeX, newSizeY;\r
5854   HDWP hdwp;\r
5855 } ResizeEditPlusButtonsClosure;\r
5856 \r
5857 BOOL CALLBACK\r
5858 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5859 {\r
5860   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5861   RECT rect;\r
5862   POINT pt;\r
5863 \r
5864   if (hChild == cl->hText) return TRUE;\r
5865   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5866   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5867   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5868   ScreenToClient(cl->hDlg, &pt);\r
5869   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5870     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5871   return TRUE;\r
5872 }\r
5873 \r
5874 /* Resize a dialog that has a (rich) edit field filling most of\r
5875    the top, with a row of buttons below */\r
5876 VOID\r
5877 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5878 {\r
5879   RECT rectText;\r
5880   int newTextHeight, newTextWidth;\r
5881   ResizeEditPlusButtonsClosure cl;\r
5882   \r
5883   /*if (IsIconic(hDlg)) return;*/\r
5884   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5885   \r
5886   cl.hdwp = BeginDeferWindowPos(8);\r
5887 \r
5888   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5889   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5890   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5891   if (newTextHeight < 0) {\r
5892     newSizeY += -newTextHeight;\r
5893     newTextHeight = 0;\r
5894   }\r
5895   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5896     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5897 \r
5898   cl.hDlg = hDlg;\r
5899   cl.hText = hText;\r
5900   cl.sizeX = sizeX;\r
5901   cl.sizeY = sizeY;\r
5902   cl.newSizeX = newSizeX;\r
5903   cl.newSizeY = newSizeY;\r
5904   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5905 \r
5906   EndDeferWindowPos(cl.hdwp);\r
5907 }\r
5908 \r
5909 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5910 {\r
5911     RECT    rChild, rParent;\r
5912     int     wChild, hChild, wParent, hParent;\r
5913     int     wScreen, hScreen, xNew, yNew;\r
5914     HDC     hdc;\r
5915 \r
5916     /* Get the Height and Width of the child window */\r
5917     GetWindowRect (hwndChild, &rChild);\r
5918     wChild = rChild.right - rChild.left;\r
5919     hChild = rChild.bottom - rChild.top;\r
5920 \r
5921     /* Get the Height and Width of the parent window */\r
5922     GetWindowRect (hwndParent, &rParent);\r
5923     wParent = rParent.right - rParent.left;\r
5924     hParent = rParent.bottom - rParent.top;\r
5925 \r
5926     /* Get the display limits */\r
5927     hdc = GetDC (hwndChild);\r
5928     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5929     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5930     ReleaseDC(hwndChild, hdc);\r
5931 \r
5932     /* Calculate new X position, then adjust for screen */\r
5933     xNew = rParent.left + ((wParent - wChild) /2);\r
5934     if (xNew < 0) {\r
5935         xNew = 0;\r
5936     } else if ((xNew+wChild) > wScreen) {\r
5937         xNew = wScreen - wChild;\r
5938     }\r
5939 \r
5940     /* Calculate new Y position, then adjust for screen */\r
5941     if( mode == 0 ) {\r
5942         yNew = rParent.top  + ((hParent - hChild) /2);\r
5943     }\r
5944     else {\r
5945         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5946     }\r
5947 \r
5948     if (yNew < 0) {\r
5949         yNew = 0;\r
5950     } else if ((yNew+hChild) > hScreen) {\r
5951         yNew = hScreen - hChild;\r
5952     }\r
5953 \r
5954     /* Set it, and return */\r
5955     return SetWindowPos (hwndChild, NULL,\r
5956                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5957 }\r
5958 \r
5959 /* Center one window over another */\r
5960 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5961 {\r
5962     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5963 }\r
5964 \r
5965 /*---------------------------------------------------------------------------*\\r
5966  *\r
5967  * Startup Dialog functions\r
5968  *\r
5969 \*---------------------------------------------------------------------------*/\r
5970 void\r
5971 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5972 {\r
5973   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5974 \r
5975   while (*cd != NULL) {\r
5976     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5977     cd++;\r
5978   }\r
5979 }\r
5980 \r
5981 void\r
5982 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5983 {\r
5984   char buf1[MAX_ARG_LEN];\r
5985   int len;\r
5986 \r
5987   if (str[0] == '@') {\r
5988     FILE* f = fopen(str + 1, "r");\r
5989     if (f == NULL) {\r
5990       DisplayFatalError(str + 1, errno, 2);\r
5991       return;\r
5992     }\r
5993     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5994     fclose(f);\r
5995     buf1[len] = NULLCHAR;\r
5996     str = buf1;\r
5997   }\r
5998 \r
5999   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6000 \r
6001   for (;;) {\r
6002     char buf[MSG_SIZ];\r
6003     char *end = strchr(str, '\n');\r
6004     if (end == NULL) return;\r
6005     memcpy(buf, str, end - str);\r
6006     buf[end - str] = NULLCHAR;\r
6007     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6008     str = end + 1;\r
6009   }\r
6010 }\r
6011 \r
6012 void\r
6013 SetStartupDialogEnables(HWND hDlg)\r
6014 {\r
6015   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6016     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6017     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6018   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6019     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6020   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6021     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6022   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6023     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6024   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6025     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6026     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6027     IsDlgButtonChecked(hDlg, OPT_View));\r
6028 }\r
6029 \r
6030 char *\r
6031 QuoteForFilename(char *filename)\r
6032 {\r
6033   int dquote, space;\r
6034   dquote = strchr(filename, '"') != NULL;\r
6035   space = strchr(filename, ' ') != NULL;\r
6036   if (dquote || space) {\r
6037     if (dquote) {\r
6038       return "'";\r
6039     } else {\r
6040       return "\"";\r
6041     }\r
6042   } else {\r
6043     return "";\r
6044   }\r
6045 }\r
6046 \r
6047 VOID\r
6048 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6049 {\r
6050   char buf[MSG_SIZ];\r
6051   char *q;\r
6052 \r
6053   InitComboStringsFromOption(hwndCombo, nthnames);\r
6054   q = QuoteForFilename(nthcp);\r
6055     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6056   if (*nthdir != NULLCHAR) {\r
6057     q = QuoteForFilename(nthdir);\r
6058       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6059   }\r
6060   if (*nthcp == NULLCHAR) {\r
6061     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6062   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6063     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6064     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6065   }\r
6066 }\r
6067 \r
6068 LRESULT CALLBACK\r
6069 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6070 {\r
6071   char buf[MSG_SIZ];\r
6072   HANDLE hwndCombo;\r
6073   char *p;\r
6074 \r
6075   switch (message) {\r
6076   case WM_INITDIALOG:\r
6077     /* Center the dialog */\r
6078     CenterWindow (hDlg, GetDesktopWindow());\r
6079     Translate(hDlg, DLG_Startup);\r
6080     /* Initialize the dialog items */\r
6081     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6082                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6083                   firstChessProgramNames);\r
6084     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6085                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6086                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6087     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6088     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6089       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6090     if (*appData.icsHelper != NULLCHAR) {\r
6091       char *q = QuoteForFilename(appData.icsHelper);\r
6092       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6093     }\r
6094     if (*appData.icsHost == NULLCHAR) {\r
6095       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6096       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6097     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6098       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6099       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6100     }\r
6101 \r
6102     if (appData.icsActive) {\r
6103       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6104     }\r
6105     else if (appData.noChessProgram) {\r
6106       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6107     }\r
6108     else {\r
6109       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6110     }\r
6111 \r
6112     SetStartupDialogEnables(hDlg);\r
6113     return TRUE;\r
6114 \r
6115   case WM_COMMAND:\r
6116     switch (LOWORD(wParam)) {\r
6117     case IDOK:\r
6118       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6119         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6120         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6121         p = buf;\r
6122         ParseArgs(StringGet, &p);\r
6123         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6124         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6125         p = buf;
6126         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6127         ParseArgs(StringGet, &p);\r
6128         SwapEngines(singleList); // ... and then make it 'second'\r
6129         appData.noChessProgram = FALSE;\r
6130         appData.icsActive = FALSE;\r
6131       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6132         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6133         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6134         p = buf;\r
6135         ParseArgs(StringGet, &p);\r
6136         if (appData.zippyPlay) {\r
6137           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6138           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6139           p = buf;\r
6140           ParseArgs(StringGet, &p);\r
6141         }\r
6142       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6143         appData.noChessProgram = TRUE;\r
6144         appData.icsActive = FALSE;\r
6145       } else {\r
6146         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6147                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6148         return TRUE;\r
6149       }\r
6150       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6151         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6152         p = buf;\r
6153         ParseArgs(StringGet, &p);\r
6154       }\r
6155       EndDialog(hDlg, TRUE);\r
6156       return TRUE;\r
6157 \r
6158     case IDCANCEL:\r
6159       ExitEvent(0);\r
6160       return TRUE;\r
6161 \r
6162     case IDM_HELPCONTENTS:\r
6163       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6164         MessageBox (GetFocus(),\r
6165                     _("Unable to activate help"),\r
6166                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6167       }\r
6168       break;\r
6169 \r
6170     default:\r
6171       SetStartupDialogEnables(hDlg);\r
6172       break;\r
6173     }\r
6174     break;\r
6175   }\r
6176   return FALSE;\r
6177 }\r
6178 \r
6179 /*---------------------------------------------------------------------------*\\r
6180  *\r
6181  * About box dialog functions\r
6182  *\r
6183 \*---------------------------------------------------------------------------*/\r
6184 \r
6185 /* Process messages for "About" dialog box */\r
6186 LRESULT CALLBACK\r
6187 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6188 {\r
6189   switch (message) {\r
6190   case WM_INITDIALOG: /* message: initialize dialog box */\r
6191     /* Center the dialog over the application window */\r
6192     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6193     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6194     Translate(hDlg, ABOUTBOX);\r
6195     JAWS_COPYRIGHT\r
6196     return (TRUE);\r
6197 \r
6198   case WM_COMMAND: /* message: received a command */\r
6199     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6200         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6201       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6202       return (TRUE);\r
6203     }\r
6204     break;\r
6205   }\r
6206   return (FALSE);\r
6207 }\r
6208 \r
6209 /*---------------------------------------------------------------------------*\\r
6210  *\r
6211  * Comment Dialog functions\r
6212  *\r
6213 \*---------------------------------------------------------------------------*/\r
6214 \r
6215 LRESULT CALLBACK\r
6216 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6217 {\r
6218   static HANDLE hwndText = NULL;\r
6219   int len, newSizeX, newSizeY, flags;\r
6220   static int sizeX, sizeY;\r
6221   char *str;\r
6222   RECT rect;\r
6223   MINMAXINFO *mmi;\r
6224 \r
6225   switch (message) {\r
6226   case WM_INITDIALOG: /* message: initialize dialog box */\r
6227     /* Initialize the dialog items */\r
6228     Translate(hDlg, DLG_EditComment);\r
6229     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6230     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6231     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6232     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6233     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6234     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6235     SetWindowText(hDlg, commentTitle);\r
6236     if (editComment) {\r
6237       SetFocus(hwndText);\r
6238     } else {\r
6239       SetFocus(GetDlgItem(hDlg, IDOK));\r
6240     }\r
6241     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6242                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6243                 MAKELPARAM(FALSE, 0));\r
6244     /* Size and position the dialog */\r
6245     if (!commentDialog) {\r
6246       commentDialog = hDlg;\r
6247       flags = SWP_NOZORDER;\r
6248       GetClientRect(hDlg, &rect);\r
6249       sizeX = rect.right;\r
6250       sizeY = rect.bottom;\r
6251       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6252           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6253         WINDOWPLACEMENT wp;\r
6254         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6255         wp.length = sizeof(WINDOWPLACEMENT);\r
6256         wp.flags = 0;\r
6257         wp.showCmd = SW_SHOW;\r
6258         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6259         wp.rcNormalPosition.left = wpComment.x;\r
6260         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6261         wp.rcNormalPosition.top = wpComment.y;\r
6262         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6263         SetWindowPlacement(hDlg, &wp);\r
6264 \r
6265         GetClientRect(hDlg, &rect);\r
6266         newSizeX = rect.right;\r
6267         newSizeY = rect.bottom;\r
6268         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6269                               newSizeX, newSizeY);\r
6270         sizeX = newSizeX;\r
6271         sizeY = newSizeY;\r
6272       }\r
6273     }\r
6274     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6275     return FALSE;\r
6276 \r
6277   case WM_COMMAND: /* message: received a command */\r
6278     switch (LOWORD(wParam)) {\r
6279     case IDOK:\r
6280       if (editComment) {\r
6281         char *p, *q;\r
6282         /* Read changed options from the dialog box */\r
6283         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6284         len = GetWindowTextLength(hwndText);\r
6285         str = (char *) malloc(len + 1);\r
6286         GetWindowText(hwndText, str, len + 1);\r
6287         p = q = str;\r
6288         while (*q) {\r
6289           if (*q == '\r')\r
6290             q++;\r
6291           else\r
6292             *p++ = *q++;\r
6293         }\r
6294         *p = NULLCHAR;\r
6295         ReplaceComment(commentIndex, str);\r
6296         free(str);\r
6297       }\r
6298       CommentPopDown();\r
6299       return TRUE;\r
6300 \r
6301     case IDCANCEL:\r
6302     case OPT_CancelComment:\r
6303       CommentPopDown();\r
6304       return TRUE;\r
6305 \r
6306     case OPT_ClearComment:\r
6307       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6308       break;\r
6309 \r
6310     case OPT_EditComment:\r
6311       EditCommentEvent();\r
6312       return TRUE;\r
6313 \r
6314     default:\r
6315       break;\r
6316     }\r
6317     break;\r
6318 \r
6319   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6320         if( wParam == OPT_CommentText ) {\r
6321             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6322 \r
6323             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6324                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6325                 POINTL pt;\r
6326                 LRESULT index;\r
6327 \r
6328                 pt.x = LOWORD( lpMF->lParam );\r
6329                 pt.y = HIWORD( lpMF->lParam );\r
6330 \r
6331                 if(lpMF->msg == WM_CHAR) {\r
6332                         CHARRANGE sel;\r
6333                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6334                         index = sel.cpMin;\r
6335                 } else\r
6336                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6337 \r
6338                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6339                 len = GetWindowTextLength(hwndText);\r
6340                 str = (char *) malloc(len + 1);\r
6341                 GetWindowText(hwndText, str, len + 1);\r
6342                 ReplaceComment(commentIndex, str);\r
6343                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6344                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6345                 free(str);\r
6346 \r
6347                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6348                 lpMF->msg = WM_USER;\r
6349 \r
6350                 return TRUE;\r
6351             }\r
6352         }\r
6353         break;\r
6354 \r
6355   case WM_SIZE:\r
6356     newSizeX = LOWORD(lParam);\r
6357     newSizeY = HIWORD(lParam);\r
6358     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6359     sizeX = newSizeX;\r
6360     sizeY = newSizeY;\r
6361     break;\r
6362 \r
6363   case WM_GETMINMAXINFO:\r
6364     /* Prevent resizing window too small */\r
6365     mmi = (MINMAXINFO *) lParam;\r
6366     mmi->ptMinTrackSize.x = 100;\r
6367     mmi->ptMinTrackSize.y = 100;\r
6368     break;\r
6369   }\r
6370   return FALSE;\r
6371 }\r
6372 \r
6373 VOID\r
6374 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6375 {\r
6376   FARPROC lpProc;\r
6377   char *p, *q;\r
6378 \r
6379   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6380 \r
6381   if (str == NULL) str = "";\r
6382   p = (char *) malloc(2 * strlen(str) + 2);\r
6383   q = p;\r
6384   while (*str) {\r
6385     if (*str == '\n') *q++ = '\r';\r
6386     *q++ = *str++;\r
6387   }\r
6388   *q = NULLCHAR;\r
6389   if (commentText != NULL) free(commentText);\r
6390 \r
6391   commentIndex = index;\r
6392   commentTitle = title;\r
6393   commentText = p;\r
6394   editComment = edit;\r
6395 \r
6396   if (commentDialog) {\r
6397     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6398     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6399   } else {\r
6400     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6401     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6402                  hwndMain, (DLGPROC)lpProc);\r
6403     FreeProcInstance(lpProc);\r
6404   }\r
6405   commentUp = TRUE;\r
6406 }\r
6407 \r
6408 \r
6409 /*---------------------------------------------------------------------------*\\r
6410  *\r
6411  * Type-in move dialog functions\r
6412  * \r
6413 \*---------------------------------------------------------------------------*/\r
6414 \r
6415 LRESULT CALLBACK\r
6416 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6417 {\r
6418   char move[MSG_SIZ];\r
6419   HWND hInput;\r
6420 \r
6421   switch (message) {\r
6422   case WM_INITDIALOG:\r
6423     move[0] = (char) lParam;\r
6424     move[1] = NULLCHAR;\r
6425     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6426     Translate(hDlg, DLG_TypeInMove);\r
6427     hInput = GetDlgItem(hDlg, OPT_Move);\r
6428     SetWindowText(hInput, move);\r
6429     SetFocus(hInput);\r
6430     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6431     return FALSE;\r
6432 \r
6433   case WM_COMMAND:\r
6434     switch (LOWORD(wParam)) {\r
6435     case IDOK:
6436 \r
6437       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6438       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6439       TypeInDoneEvent(move);\r
6440       EndDialog(hDlg, TRUE);\r
6441       return TRUE;\r
6442     case IDCANCEL:\r
6443       EndDialog(hDlg, FALSE);\r
6444       return TRUE;\r
6445     default:\r
6446       break;\r
6447     }\r
6448     break;\r
6449   }\r
6450   return FALSE;\r
6451 }\r
6452 \r
6453 VOID\r
6454 PopUpMoveDialog(char firstchar)\r
6455 {\r
6456     FARPROC lpProc;\r
6457 \r
6458       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6459       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6460         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6461       FreeProcInstance(lpProc);\r
6462 }\r
6463 \r
6464 /*---------------------------------------------------------------------------*\\r
6465  *\r
6466  * Type-in name dialog functions\r
6467  * \r
6468 \*---------------------------------------------------------------------------*/\r
6469 \r
6470 LRESULT CALLBACK\r
6471 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6472 {\r
6473   char move[MSG_SIZ];\r
6474   HWND hInput;\r
6475 \r
6476   switch (message) {\r
6477   case WM_INITDIALOG:\r
6478     move[0] = (char) lParam;\r
6479     move[1] = NULLCHAR;\r
6480     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6481     Translate(hDlg, DLG_TypeInName);\r
6482     hInput = GetDlgItem(hDlg, OPT_Name);\r
6483     SetWindowText(hInput, move);\r
6484     SetFocus(hInput);\r
6485     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6486     return FALSE;\r
6487 \r
6488   case WM_COMMAND:\r
6489     switch (LOWORD(wParam)) {\r
6490     case IDOK:\r
6491       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6492       appData.userName = strdup(move);\r
6493       SetUserLogo();\r
6494       SetGameInfo();\r
6495       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6496         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6497         DisplayTitle(move);\r
6498       }\r
6499 \r
6500 \r
6501       EndDialog(hDlg, TRUE);\r
6502       return TRUE;\r
6503     case IDCANCEL:\r
6504       EndDialog(hDlg, FALSE);\r
6505       return TRUE;\r
6506     default:\r
6507       break;\r
6508     }\r
6509     break;\r
6510   }\r
6511   return FALSE;\r
6512 }\r
6513 \r
6514 VOID\r
6515 PopUpNameDialog(char firstchar)\r
6516 {\r
6517     FARPROC lpProc;\r
6518     \r
6519       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6520       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6521         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6522       FreeProcInstance(lpProc);\r
6523 }\r
6524 \r
6525 /*---------------------------------------------------------------------------*\\r
6526  *\r
6527  *  Error dialogs\r
6528  * \r
6529 \*---------------------------------------------------------------------------*/\r
6530 \r
6531 /* Nonmodal error box */\r
6532 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6533                              WPARAM wParam, LPARAM lParam);\r
6534 \r
6535 VOID\r
6536 ErrorPopUp(char *title, char *content)\r
6537 {\r
6538   FARPROC lpProc;\r
6539   char *p, *q;\r
6540   BOOLEAN modal = hwndMain == NULL;\r
6541 \r
6542   p = content;\r
6543   q = errorMessage;\r
6544   while (*p) {\r
6545     if (*p == '\n') {\r
6546       if (modal) {\r
6547         *q++ = ' ';\r
6548         p++;\r
6549       } else {\r
6550         *q++ = '\r';\r
6551         *q++ = *p++;\r
6552       }\r
6553     } else {\r
6554       *q++ = *p++;\r
6555     }\r
6556   }\r
6557   *q = NULLCHAR;\r
6558   strncpy(errorTitle, title, sizeof(errorTitle));\r
6559   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6560   \r
6561   if (modal) {\r
6562     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6563   } else {\r
6564     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6565     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6566                  hwndMain, (DLGPROC)lpProc);\r
6567     FreeProcInstance(lpProc);\r
6568   }\r
6569 }\r
6570 \r
6571 VOID\r
6572 ErrorPopDown()\r
6573 {\r
6574   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6575   if (errorDialog == NULL) return;\r
6576   DestroyWindow(errorDialog);\r
6577   errorDialog = NULL;\r
6578   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6579 }\r
6580 \r
6581 LRESULT CALLBACK\r
6582 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6583 {\r
6584   HANDLE hwndText;\r
6585   RECT rChild;\r
6586 \r
6587   switch (message) {\r
6588   case WM_INITDIALOG:\r
6589     GetWindowRect(hDlg, &rChild);\r
6590 \r
6591     /*\r
6592     SetWindowPos(hDlg, NULL, rChild.left,\r
6593       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6594       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6595     */\r
6596 \r
6597     /* \r
6598         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6599         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6600         and it doesn't work when you resize the dialog.\r
6601         For now, just give it a default position.\r
6602     */\r
6603     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6604     Translate(hDlg, DLG_Error);\r
6605 \r
6606     errorDialog = hDlg;\r
6607     SetWindowText(hDlg, errorTitle);\r
6608     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6609     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6610     return FALSE;\r
6611 \r
6612   case WM_COMMAND:\r
6613     switch (LOWORD(wParam)) {\r
6614     case IDOK:\r
6615     case IDCANCEL:\r
6616       if (errorDialog == hDlg) errorDialog = NULL;\r
6617       DestroyWindow(hDlg);\r
6618       return TRUE;\r
6619 \r
6620     default:\r
6621       break;\r
6622     }\r
6623     break;\r
6624   }\r
6625   return FALSE;\r
6626 }\r
6627 \r
6628 #ifdef GOTHIC\r
6629 HWND gothicDialog = NULL;\r
6630 \r
6631 LRESULT CALLBACK\r
6632 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6633 {\r
6634   HANDLE hwndText;\r
6635   RECT rChild;\r
6636   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6637 \r
6638   switch (message) {\r
6639   case WM_INITDIALOG:\r
6640     GetWindowRect(hDlg, &rChild);\r
6641 \r
6642     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6643                                                              SWP_NOZORDER);\r
6644 \r
6645     /* \r
6646         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6647         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6648         and it doesn't work when you resize the dialog.\r
6649         For now, just give it a default position.\r
6650     */\r
6651     gothicDialog = hDlg;\r
6652     SetWindowText(hDlg, errorTitle);\r
6653     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6654     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6655     return FALSE;\r
6656 \r
6657   case WM_COMMAND:\r
6658     switch (LOWORD(wParam)) {\r
6659     case IDOK:\r
6660     case IDCANCEL:\r
6661       if (errorDialog == hDlg) errorDialog = NULL;\r
6662       DestroyWindow(hDlg);\r
6663       return TRUE;\r
6664 \r
6665     default:\r
6666       break;\r
6667     }\r
6668     break;\r
6669   }\r
6670   return FALSE;\r
6671 }\r
6672 \r
6673 VOID\r
6674 GothicPopUp(char *title, VariantClass variant)\r
6675 {\r
6676   FARPROC lpProc;\r
6677   static char *lastTitle;\r
6678 \r
6679   strncpy(errorTitle, title, sizeof(errorTitle));\r
6680   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6681 \r
6682   if(lastTitle != title && gothicDialog != NULL) {\r
6683     DestroyWindow(gothicDialog);\r
6684     gothicDialog = NULL;\r
6685   }\r
6686   if(variant != VariantNormal && gothicDialog == NULL) {\r
6687     title = lastTitle;\r
6688     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6689     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6690                  hwndMain, (DLGPROC)lpProc);\r
6691     FreeProcInstance(lpProc);\r
6692   }\r
6693 }\r
6694 #endif\r
6695 \r
6696 /*---------------------------------------------------------------------------*\\r
6697  *\r
6698  *  Ics Interaction console functions\r
6699  *\r
6700 \*---------------------------------------------------------------------------*/\r
6701 \r
6702 #define HISTORY_SIZE 64\r
6703 static char *history[HISTORY_SIZE];\r
6704 int histIn = 0, histP = 0;\r
6705 \r
6706 VOID\r
6707 SaveInHistory(char *cmd)\r
6708 {\r
6709   if (history[histIn] != NULL) {\r
6710     free(history[histIn]);\r
6711     history[histIn] = NULL;\r
6712   }\r
6713   if (*cmd == NULLCHAR) return;\r
6714   history[histIn] = StrSave(cmd);\r
6715   histIn = (histIn + 1) % HISTORY_SIZE;\r
6716   if (history[histIn] != NULL) {\r
6717     free(history[histIn]);\r
6718     history[histIn] = NULL;\r
6719   }\r
6720   histP = histIn;\r
6721 }\r
6722 \r
6723 char *\r
6724 PrevInHistory(char *cmd)\r
6725 {\r
6726   int newhp;\r
6727   if (histP == histIn) {\r
6728     if (history[histIn] != NULL) free(history[histIn]);\r
6729     history[histIn] = StrSave(cmd);\r
6730   }\r
6731   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6732   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6733   histP = newhp;\r
6734   return history[histP];\r
6735 }\r
6736 \r
6737 char *\r
6738 NextInHistory()\r
6739 {\r
6740   if (histP == histIn) return NULL;\r
6741   histP = (histP + 1) % HISTORY_SIZE;\r
6742   return history[histP];   \r
6743 }\r
6744 \r
6745 HMENU\r
6746 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6747 {\r
6748   HMENU hmenu, h;\r
6749   int i = 0;\r
6750   hmenu = LoadMenu(hInst, "TextMenu");\r
6751   h = GetSubMenu(hmenu, 0);\r
6752   while (e->item) {\r
6753     if (strcmp(e->item, "-") == 0) {\r
6754       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6755     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6756       int flags = MF_STRING, j = 0;\r
6757       if (e->item[0] == '|') {\r
6758         flags |= MF_MENUBARBREAK;\r
6759         j++;\r
6760       }\r
6761       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6762       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6763     }\r
6764     e++;\r
6765     i++;\r
6766   } \r
6767   return hmenu;\r
6768 }\r
6769 \r
6770 WNDPROC consoleTextWindowProc;\r
6771 \r
6772 void\r
6773 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6774 {\r
6775   char buf[MSG_SIZ], name[MSG_SIZ];\r
6776   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6777   CHARRANGE sel;\r
6778 \r
6779   if (!getname) {\r
6780     SetWindowText(hInput, command);\r
6781     if (immediate) {\r
6782       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6783     } else {\r
6784       sel.cpMin = 999999;\r
6785       sel.cpMax = 999999;\r
6786       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6787       SetFocus(hInput);\r
6788     }\r
6789     return;\r
6790   }    \r
6791   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6792   if (sel.cpMin == sel.cpMax) {\r
6793     /* Expand to surrounding word */\r
6794     TEXTRANGE tr;\r
6795     do {\r
6796       tr.chrg.cpMax = sel.cpMin;\r
6797       tr.chrg.cpMin = --sel.cpMin;\r
6798       if (sel.cpMin < 0) break;\r
6799       tr.lpstrText = name;\r
6800       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6801     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6802     sel.cpMin++;\r
6803 \r
6804     do {\r
6805       tr.chrg.cpMin = sel.cpMax;\r
6806       tr.chrg.cpMax = ++sel.cpMax;\r
6807       tr.lpstrText = name;\r
6808       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6809     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6810     sel.cpMax--;\r
6811 \r
6812     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6813       MessageBeep(MB_ICONEXCLAMATION);\r
6814       return;\r
6815     }\r
6816     tr.chrg = sel;\r
6817     tr.lpstrText = name;\r
6818     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6819   } else {\r
6820     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6821       MessageBeep(MB_ICONEXCLAMATION);\r
6822       return;\r
6823     }\r
6824     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6825   }\r
6826   if (immediate) {\r
6827     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6828     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6829     SetWindowText(hInput, buf);\r
6830     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6831   } else {\r
6832     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6833       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6834     SetWindowText(hInput, buf);\r
6835     sel.cpMin = 999999;\r
6836     sel.cpMax = 999999;\r
6837     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6838     SetFocus(hInput);\r
6839   }\r
6840 }\r
6841 \r
6842 LRESULT CALLBACK \r
6843 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6844 {\r
6845   HWND hInput;\r
6846   CHARRANGE sel;\r
6847 \r
6848   switch (message) {\r
6849   case WM_KEYDOWN:\r
6850     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6851     if(wParam=='R') return 0;\r
6852     switch (wParam) {\r
6853     case VK_PRIOR:\r
6854       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6855       return 0;\r
6856     case VK_NEXT:\r
6857       sel.cpMin = 999999;\r
6858       sel.cpMax = 999999;\r
6859       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6860       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6861       return 0;\r
6862     }\r
6863     break;\r
6864   case WM_CHAR:\r
6865    if(wParam != '\022') {\r
6866     if (wParam == '\t') {\r
6867       if (GetKeyState(VK_SHIFT) < 0) {\r
6868         /* shifted */\r
6869         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6870         if (buttonDesc[0].hwnd) {\r
6871           SetFocus(buttonDesc[0].hwnd);\r
6872         } else {\r
6873           SetFocus(hwndMain);\r
6874         }\r
6875       } else {\r
6876         /* unshifted */\r
6877         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6878       }\r
6879     } else {\r
6880       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6881       JAWS_DELETE( SetFocus(hInput); )\r
6882       SendMessage(hInput, message, wParam, lParam);\r
6883     }\r
6884     return 0;\r
6885    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6886    lParam = -1;\r
6887   case WM_RBUTTONDOWN:\r
6888     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6889       /* Move selection here if it was empty */\r
6890       POINT pt;\r
6891       pt.x = LOWORD(lParam);\r
6892       pt.y = HIWORD(lParam);\r
6893       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6894       if (sel.cpMin == sel.cpMax) {\r
6895         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6896         sel.cpMax = sel.cpMin;\r
6897         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6898       }\r
6899       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6900 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6901       POINT pt;\r
6902       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6903       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6904       if (sel.cpMin == sel.cpMax) {\r
6905         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6906         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6907       }\r
6908       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6909         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6910       }\r
6911       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6912       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6913       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6914       MenuPopup(hwnd, pt, hmenu, -1);\r
6915 }\r
6916     }\r
6917     return 0;\r
6918   case WM_RBUTTONUP:\r
6919     if (GetKeyState(VK_SHIFT) & ~1) {\r
6920       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6921         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6922     }\r
6923     return 0;\r
6924   case WM_PASTE:\r
6925     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6926     SetFocus(hInput);\r
6927     return SendMessage(hInput, message, wParam, lParam);\r
6928   case WM_MBUTTONDOWN:\r
6929     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6930   case WM_COMMAND:\r
6931     switch (LOWORD(wParam)) {\r
6932     case IDM_QuickPaste:\r
6933       {\r
6934         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6935         if (sel.cpMin == sel.cpMax) {\r
6936           MessageBeep(MB_ICONEXCLAMATION);\r
6937           return 0;\r
6938         }\r
6939         SendMessage(hwnd, WM_COPY, 0, 0);\r
6940         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6941         SendMessage(hInput, WM_PASTE, 0, 0);\r
6942         SetFocus(hInput);\r
6943         return 0;\r
6944       }\r
6945     case IDM_Cut:\r
6946       SendMessage(hwnd, WM_CUT, 0, 0);\r
6947       return 0;\r
6948     case IDM_Paste:\r
6949       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6950       return 0;\r
6951     case IDM_Copy:\r
6952       SendMessage(hwnd, WM_COPY, 0, 0);\r
6953       return 0;\r
6954     default:\r
6955       {\r
6956         int i = LOWORD(wParam) - IDM_CommandX;\r
6957         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6958             icsTextMenuEntry[i].command != NULL) {\r
6959           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6960                    icsTextMenuEntry[i].getname,\r
6961                    icsTextMenuEntry[i].immediate);\r
6962           return 0;\r
6963         }\r
6964       }\r
6965       break;\r
6966     }\r
6967     break;\r
6968   }\r
6969   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6970 }\r
6971 \r
6972 WNDPROC consoleInputWindowProc;\r
6973 \r
6974 LRESULT CALLBACK\r
6975 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6976 {\r
6977   char buf[MSG_SIZ];\r
6978   char *p;\r
6979   static BOOL sendNextChar = FALSE;\r
6980   static BOOL quoteNextChar = FALSE;\r
6981   InputSource *is = consoleInputSource;\r
6982   CHARFORMAT cf;\r
6983   CHARRANGE sel;\r
6984 \r
6985   switch (message) {\r
6986   case WM_CHAR:\r
6987     if (!appData.localLineEditing || sendNextChar) {\r
6988       is->buf[0] = (CHAR) wParam;\r
6989       is->count = 1;\r
6990       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6991       sendNextChar = FALSE;\r
6992       return 0;\r
6993     }\r
6994     if (quoteNextChar) {\r
6995       buf[0] = (char) wParam;\r
6996       buf[1] = NULLCHAR;\r
6997       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6998       quoteNextChar = FALSE;\r
6999       return 0;\r
7000     }\r
7001     switch (wParam) {\r
7002     case '\r':   /* Enter key */\r
7003       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7004       if (consoleEcho) SaveInHistory(is->buf);\r
7005       is->buf[is->count++] = '\n';\r
7006       is->buf[is->count] = NULLCHAR;\r
7007       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7008       if (consoleEcho) {\r
7009         ConsoleOutput(is->buf, is->count, TRUE);\r
7010       } else if (appData.localLineEditing) {\r
7011         ConsoleOutput("\n", 1, TRUE);\r
7012       }\r
7013       /* fall thru */\r
7014     case '\033': /* Escape key */\r
7015       SetWindowText(hwnd, "");\r
7016       cf.cbSize = sizeof(CHARFORMAT);\r
7017       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7018       if (consoleEcho) {\r
7019         cf.crTextColor = textAttribs[ColorNormal].color;\r
7020       } else {\r
7021         cf.crTextColor = COLOR_ECHOOFF;\r
7022       }\r
7023       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7024       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7025       return 0;\r
7026     case '\t':   /* Tab key */\r
7027       if (GetKeyState(VK_SHIFT) < 0) {\r
7028         /* shifted */\r
7029         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7030       } else {\r
7031         /* unshifted */\r
7032         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7033         if (buttonDesc[0].hwnd) {\r
7034           SetFocus(buttonDesc[0].hwnd);\r
7035         } else {\r
7036           SetFocus(hwndMain);\r
7037         }\r
7038       }\r
7039       return 0;\r
7040     case '\023': /* Ctrl+S */\r
7041       sendNextChar = TRUE;\r
7042       return 0;\r
7043     case '\021': /* Ctrl+Q */\r
7044       quoteNextChar = TRUE;\r
7045       return 0;\r
7046     JAWS_REPLAY\r
7047     default:\r
7048       break;\r
7049     }\r
7050     break;\r
7051   case WM_KEYDOWN:\r
7052     switch (wParam) {\r
7053     case VK_UP:\r
7054       GetWindowText(hwnd, buf, MSG_SIZ);\r
7055       p = PrevInHistory(buf);\r
7056       if (p != NULL) {\r
7057         SetWindowText(hwnd, p);\r
7058         sel.cpMin = 999999;\r
7059         sel.cpMax = 999999;\r
7060         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7061         return 0;\r
7062       }\r
7063       break;\r
7064     case VK_DOWN:\r
7065       p = NextInHistory();\r
7066       if (p != NULL) {\r
7067         SetWindowText(hwnd, p);\r
7068         sel.cpMin = 999999;\r
7069         sel.cpMax = 999999;\r
7070         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7071         return 0;\r
7072       }\r
7073       break;\r
7074     case VK_HOME:\r
7075     case VK_END:\r
7076       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7077       /* fall thru */\r
7078     case VK_PRIOR:\r
7079     case VK_NEXT:\r
7080       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7081       return 0;\r
7082     }\r
7083     break;\r
7084   case WM_MBUTTONDOWN:\r
7085     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7086       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7087     break;\r
7088   case WM_RBUTTONUP:\r
7089     if (GetKeyState(VK_SHIFT) & ~1) {\r
7090       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7091         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7092     } else {\r
7093       POINT pt;\r
7094       HMENU hmenu;\r
7095       hmenu = LoadMenu(hInst, "InputMenu");\r
7096       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7097       if (sel.cpMin == sel.cpMax) {\r
7098         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7099         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7100       }\r
7101       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7102         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7103       }\r
7104       pt.x = LOWORD(lParam);\r
7105       pt.y = HIWORD(lParam);\r
7106       MenuPopup(hwnd, pt, hmenu, -1);\r
7107     }\r
7108     return 0;\r
7109   case WM_COMMAND:\r
7110     switch (LOWORD(wParam)) { \r
7111     case IDM_Undo:\r
7112       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7113       return 0;\r
7114     case IDM_SelectAll:\r
7115       sel.cpMin = 0;\r
7116       sel.cpMax = -1; /*999999?*/\r
7117       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7118       return 0;\r
7119     case IDM_Cut:\r
7120       SendMessage(hwnd, WM_CUT, 0, 0);\r
7121       return 0;\r
7122     case IDM_Paste:\r
7123       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7124       return 0;\r
7125     case IDM_Copy:\r
7126       SendMessage(hwnd, WM_COPY, 0, 0);\r
7127       return 0;\r
7128     }\r
7129     break;\r
7130   }\r
7131   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7132 }\r
7133 \r
7134 #define CO_MAX  100000\r
7135 #define CO_TRIM   1000\r
7136 \r
7137 LRESULT CALLBACK\r
7138 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7139 {\r
7140   static SnapData sd;\r
7141   HWND hText, hInput;\r
7142   RECT rect;\r
7143   static int sizeX, sizeY;\r
7144   int newSizeX, newSizeY;\r
7145   MINMAXINFO *mmi;\r
7146   WORD wMask;\r
7147 \r
7148   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7149   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7150 \r
7151   switch (message) {\r
7152   case WM_NOTIFY:\r
7153     if (((NMHDR*)lParam)->code == EN_LINK)\r
7154     {\r
7155       ENLINK *pLink = (ENLINK*)lParam;\r
7156       if (pLink->msg == WM_LBUTTONUP)\r
7157       {\r
7158         TEXTRANGE tr;\r
7159 \r
7160         tr.chrg = pLink->chrg;\r
7161         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7162         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7163         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7164         free(tr.lpstrText);\r
7165       }\r
7166     }\r
7167     break;\r
7168   case WM_INITDIALOG: /* message: initialize dialog box */\r
7169     hwndConsole = hDlg;\r
7170     SetFocus(hInput);\r
7171     consoleTextWindowProc = (WNDPROC)\r
7172       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7173     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7174     consoleInputWindowProc = (WNDPROC)\r
7175       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7176     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7177     Colorize(ColorNormal, TRUE);\r
7178     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7179     ChangedConsoleFont();\r
7180     GetClientRect(hDlg, &rect);\r
7181     sizeX = rect.right;\r
7182     sizeY = rect.bottom;\r
7183     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7184         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7185       WINDOWPLACEMENT wp;\r
7186       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7187       wp.length = sizeof(WINDOWPLACEMENT);\r
7188       wp.flags = 0;\r
7189       wp.showCmd = SW_SHOW;\r
7190       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7191       wp.rcNormalPosition.left = wpConsole.x;\r
7192       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7193       wp.rcNormalPosition.top = wpConsole.y;\r
7194       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7195       SetWindowPlacement(hDlg, &wp);\r
7196     }\r
7197 \r
7198    // [HGM] Chessknight's change 2004-07-13\r
7199    else { /* Determine Defaults */\r
7200        WINDOWPLACEMENT wp;\r
7201        wpConsole.x = wpMain.width + 1;\r
7202        wpConsole.y = wpMain.y;\r
7203        wpConsole.width = screenWidth -  wpMain.width;\r
7204        wpConsole.height = wpMain.height;\r
7205        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7206        wp.length = sizeof(WINDOWPLACEMENT);\r
7207        wp.flags = 0;\r
7208        wp.showCmd = SW_SHOW;\r
7209        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7210        wp.rcNormalPosition.left = wpConsole.x;\r
7211        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7212        wp.rcNormalPosition.top = wpConsole.y;\r
7213        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7214        SetWindowPlacement(hDlg, &wp);\r
7215     }\r
7216 \r
7217    // Allow hText to highlight URLs and send notifications on them\r
7218    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7219    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7220    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7221    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7222 \r
7223     return FALSE;\r
7224 \r
7225   case WM_SETFOCUS:\r
7226     SetFocus(hInput);\r
7227     return 0;\r
7228 \r
7229   case WM_CLOSE:\r
7230     ExitEvent(0);\r
7231     /* not reached */\r
7232     break;\r
7233 \r
7234   case WM_SIZE:\r
7235     if (IsIconic(hDlg)) break;\r
7236     newSizeX = LOWORD(lParam);\r
7237     newSizeY = HIWORD(lParam);\r
7238     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7239       RECT rectText, rectInput;\r
7240       POINT pt;\r
7241       int newTextHeight, newTextWidth;\r
7242       GetWindowRect(hText, &rectText);\r
7243       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7244       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7245       if (newTextHeight < 0) {\r
7246         newSizeY += -newTextHeight;\r
7247         newTextHeight = 0;\r
7248       }\r
7249       SetWindowPos(hText, NULL, 0, 0,\r
7250         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7251       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7252       pt.x = rectInput.left;\r
7253       pt.y = rectInput.top + newSizeY - sizeY;\r
7254       ScreenToClient(hDlg, &pt);\r
7255       SetWindowPos(hInput, NULL, \r
7256         pt.x, pt.y, /* needs client coords */   \r
7257         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7258         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7259     }\r
7260     sizeX = newSizeX;\r
7261     sizeY = newSizeY;\r
7262     break;\r
7263 \r
7264   case WM_GETMINMAXINFO:\r
7265     /* Prevent resizing window too small */\r
7266     mmi = (MINMAXINFO *) lParam;\r
7267     mmi->ptMinTrackSize.x = 100;\r
7268     mmi->ptMinTrackSize.y = 100;\r
7269     break;\r
7270 \r
7271   /* [AS] Snapping */\r
7272   case WM_ENTERSIZEMOVE:\r
7273     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7274 \r
7275   case WM_SIZING:\r
7276     return OnSizing( &sd, hDlg, wParam, lParam );\r
7277 \r
7278   case WM_MOVING:\r
7279     return OnMoving( &sd, hDlg, wParam, lParam );\r
7280 \r
7281   case WM_EXITSIZEMOVE:\r
7282         UpdateICSWidth(hText);\r
7283     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7284   }\r
7285 \r
7286   return DefWindowProc(hDlg, message, wParam, lParam);\r
7287 }\r
7288 \r
7289 \r
7290 VOID\r
7291 ConsoleCreate()\r
7292 {\r
7293   HWND hCons;\r
7294   if (hwndConsole) return;\r
7295   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7296   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7297 }\r
7298 \r
7299 \r
7300 VOID\r
7301 ConsoleOutput(char* data, int length, int forceVisible)\r
7302 {\r
7303   HWND hText;\r
7304   int trim, exlen;\r
7305   char *p, *q;\r
7306   char buf[CO_MAX+1];\r
7307   POINT pEnd;\r
7308   RECT rect;\r
7309   static int delayLF = 0;\r
7310   CHARRANGE savesel, sel;\r
7311 \r
7312   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7313   p = data;\r
7314   q = buf;\r
7315   if (delayLF) {\r
7316     *q++ = '\r';\r
7317     *q++ = '\n';\r
7318     delayLF = 0;\r
7319   }\r
7320   while (length--) {\r
7321     if (*p == '\n') {\r
7322       if (*++p) {\r
7323         *q++ = '\r';\r
7324         *q++ = '\n';\r
7325       } else {\r
7326         delayLF = 1;\r
7327       }\r
7328     } else if (*p == '\007') {\r
7329        MyPlaySound(&sounds[(int)SoundBell]);\r
7330        p++;\r
7331     } else {\r
7332       *q++ = *p++;\r
7333     }\r
7334   }\r
7335   *q = NULLCHAR;\r
7336   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7337   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7338   /* Save current selection */\r
7339   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7340   exlen = GetWindowTextLength(hText);\r
7341   /* Find out whether current end of text is visible */\r
7342   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7343   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7344   /* Trim existing text if it's too long */\r
7345   if (exlen + (q - buf) > CO_MAX) {\r
7346     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7347     sel.cpMin = 0;\r
7348     sel.cpMax = trim;\r
7349     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7350     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7351     exlen -= trim;\r
7352     savesel.cpMin -= trim;\r
7353     savesel.cpMax -= trim;\r
7354     if (exlen < 0) exlen = 0;\r
7355     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7356     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7357   }\r
7358   /* Append the new text */\r
7359   sel.cpMin = exlen;\r
7360   sel.cpMax = exlen;\r
7361   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7362   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7363   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7364   if (forceVisible || exlen == 0 ||\r
7365       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7366        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7367     /* Scroll to make new end of text visible if old end of text\r
7368        was visible or new text is an echo of user typein */\r
7369     sel.cpMin = 9999999;\r
7370     sel.cpMax = 9999999;\r
7371     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7372     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7373     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7374     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7375   }\r
7376   if (savesel.cpMax == exlen || forceVisible) {\r
7377     /* Move insert point to new end of text if it was at the old\r
7378        end of text or if the new text is an echo of user typein */\r
7379     sel.cpMin = 9999999;\r
7380     sel.cpMax = 9999999;\r
7381     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7382   } else {\r
7383     /* Restore previous selection */\r
7384     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7385   }\r
7386   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7387 }\r
7388 \r
7389 /*---------*/\r
7390 \r
7391 \r
7392 void\r
7393 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7394 {\r
7395   char buf[100];\r
7396   char *str;\r
7397   COLORREF oldFg, oldBg;\r
7398   HFONT oldFont;\r
7399   RECT rect;\r
7400 \r
7401   if(copyNumber > 1)
7402     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7403 \r
7404   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7405   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7406   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7407 \r
7408   rect.left = x;\r
7409   rect.right = x + squareSize;\r
7410   rect.top  = y;\r
7411   rect.bottom = y + squareSize;\r
7412   str = buf;\r
7413 \r
7414   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7415                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7416              y, ETO_CLIPPED|ETO_OPAQUE,\r
7417              &rect, str, strlen(str), NULL);\r
7418 \r
7419   (void) SetTextColor(hdc, oldFg);\r
7420   (void) SetBkColor(hdc, oldBg);\r
7421   (void) SelectObject(hdc, oldFont);\r
7422 }\r
7423 \r
7424 void\r
7425 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7426               RECT *rect, char *color, char *flagFell)\r
7427 {\r
7428   char buf[100];\r
7429   char *str;\r
7430   COLORREF oldFg, oldBg;\r
7431   HFONT oldFont;\r
7432 \r
7433   if (appData.clockMode) {\r
7434     if (tinyLayout)\r
7435       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7436     else\r
7437       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7438     str = buf;\r
7439   } else {\r
7440     str = color;\r
7441   }\r
7442 \r
7443   if (highlight) {\r
7444     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7445     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7446   } else {\r
7447     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7448     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7449   }\r
7450   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7451 \r
7452   JAWS_SILENCE\r
7453 \r
7454   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7455              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7456              rect, str, strlen(str), NULL);\r
7457   if(logoHeight > 0 && appData.clockMode) {\r
7458       RECT r;\r
7459       str += strlen(color)+2;\r
7460       r.top = rect->top + logoHeight/2;\r
7461       r.left = rect->left;\r
7462       r.right = rect->right;\r
7463       r.bottom = rect->bottom;\r
7464       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7465                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7466                  &r, str, strlen(str), NULL);\r
7467   }\r
7468   (void) SetTextColor(hdc, oldFg);\r
7469   (void) SetBkColor(hdc, oldBg);\r
7470   (void) SelectObject(hdc, oldFont);\r
7471 }\r
7472 \r
7473 \r
7474 int\r
7475 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7476            OVERLAPPED *ovl)\r
7477 {\r
7478   int ok, err;\r
7479 \r
7480   /* [AS]  */\r
7481   if( count <= 0 ) {\r
7482     if (appData.debugMode) {\r
7483       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7484     }\r
7485 \r
7486     return ERROR_INVALID_USER_BUFFER;\r
7487   }\r
7488 \r
7489   ResetEvent(ovl->hEvent);\r
7490   ovl->Offset = ovl->OffsetHigh = 0;\r
7491   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7492   if (ok) {\r
7493     err = NO_ERROR;\r
7494   } else {\r
7495     err = GetLastError();\r
7496     if (err == ERROR_IO_PENDING) {\r
7497       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7498       if (ok)\r
7499         err = NO_ERROR;\r
7500       else\r
7501         err = GetLastError();\r
7502     }\r
7503   }\r
7504   return err;\r
7505 }\r
7506 \r
7507 int\r
7508 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7509             OVERLAPPED *ovl)\r
7510 {\r
7511   int ok, err;\r
7512 \r
7513   ResetEvent(ovl->hEvent);\r
7514   ovl->Offset = ovl->OffsetHigh = 0;\r
7515   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7516   if (ok) {\r
7517     err = NO_ERROR;\r
7518   } else {\r
7519     err = GetLastError();\r
7520     if (err == ERROR_IO_PENDING) {\r
7521       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7522       if (ok)\r
7523         err = NO_ERROR;\r
7524       else\r
7525         err = GetLastError();\r
7526     }\r
7527   }\r
7528   return err;\r
7529 }\r
7530 \r
7531 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7532 void CheckForInputBufferFull( InputSource * is )\r
7533 {\r
7534     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7535         /* Look for end of line */\r
7536         char * p = is->buf;\r
7537         \r
7538         while( p < is->next && *p != '\n' ) {\r
7539             p++;\r
7540         }\r
7541 \r
7542         if( p >= is->next ) {\r
7543             if (appData.debugMode) {\r
7544                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7545             }\r
7546 \r
7547             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7548             is->count = (DWORD) -1;\r
7549             is->next = is->buf;\r
7550         }\r
7551     }\r
7552 }\r
7553 \r
7554 DWORD\r
7555 InputThread(LPVOID arg)\r
7556 {\r
7557   InputSource *is;\r
7558   OVERLAPPED ovl;\r
7559 \r
7560   is = (InputSource *) arg;\r
7561   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7562   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7563   while (is->hThread != NULL) {\r
7564     is->error = DoReadFile(is->hFile, is->next,\r
7565                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7566                            &is->count, &ovl);\r
7567     if (is->error == NO_ERROR) {\r
7568       is->next += is->count;\r
7569     } else {\r
7570       if (is->error == ERROR_BROKEN_PIPE) {\r
7571         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7572         is->count = 0;\r
7573       } else {\r
7574         is->count = (DWORD) -1;\r
7575         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7576         break; \r
7577       }\r
7578     }\r
7579 \r
7580     CheckForInputBufferFull( is );\r
7581 \r
7582     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7583 \r
7584     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7585 \r
7586     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7587   }\r
7588 \r
7589   CloseHandle(ovl.hEvent);\r
7590   CloseHandle(is->hFile);\r
7591 \r
7592   if (appData.debugMode) {\r
7593     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7594   }\r
7595 \r
7596   return 0;\r
7597 }\r
7598 \r
7599 \r
7600 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7601 DWORD\r
7602 NonOvlInputThread(LPVOID arg)\r
7603 {\r
7604   InputSource *is;\r
7605   char *p, *q;\r
7606   int i;\r
7607   char prev;\r
7608 \r
7609   is = (InputSource *) arg;\r
7610   while (is->hThread != NULL) {\r
7611     is->error = ReadFile(is->hFile, is->next,\r
7612                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7613                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7614     if (is->error == NO_ERROR) {\r
7615       /* Change CRLF to LF */\r
7616       if (is->next > is->buf) {\r
7617         p = is->next - 1;\r
7618         i = is->count + 1;\r
7619       } else {\r
7620         p = is->next;\r
7621         i = is->count;\r
7622       }\r
7623       q = p;\r
7624       prev = NULLCHAR;\r
7625       while (i > 0) {\r
7626         if (prev == '\r' && *p == '\n') {\r
7627           *(q-1) = '\n';\r
7628           is->count--;\r
7629         } else { \r
7630           *q++ = *p;\r
7631         }\r
7632         prev = *p++;\r
7633         i--;\r
7634       }\r
7635       *q = NULLCHAR;\r
7636       is->next = q;\r
7637     } else {\r
7638       if (is->error == ERROR_BROKEN_PIPE) {\r
7639         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7640         is->count = 0; \r
7641       } else {\r
7642         is->count = (DWORD) -1;\r
7643       }\r
7644     }\r
7645 \r
7646     CheckForInputBufferFull( is );\r
7647 \r
7648     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7649 \r
7650     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7651 \r
7652     if (is->count < 0) break;  /* Quit on error */\r
7653   }\r
7654   CloseHandle(is->hFile);\r
7655   return 0;\r
7656 }\r
7657 \r
7658 DWORD\r
7659 SocketInputThread(LPVOID arg)\r
7660 {\r
7661   InputSource *is;\r
7662 \r
7663   is = (InputSource *) arg;\r
7664   while (is->hThread != NULL) {\r
7665     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7666     if ((int)is->count == SOCKET_ERROR) {\r
7667       is->count = (DWORD) -1;\r
7668       is->error = WSAGetLastError();\r
7669     } else {\r
7670       is->error = NO_ERROR;\r
7671       is->next += is->count;\r
7672       if (is->count == 0 && is->second == is) {\r
7673         /* End of file on stderr; quit with no message */\r
7674         break;\r
7675       }\r
7676     }\r
7677     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7678 \r
7679     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7680 \r
7681     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7682   }\r
7683   return 0;\r
7684 }\r
7685 \r
7686 VOID\r
7687 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7688 {\r
7689   InputSource *is;\r
7690 \r
7691   is = (InputSource *) lParam;\r
7692   if (is->lineByLine) {\r
7693     /* Feed in lines one by one */\r
7694     char *p = is->buf;\r
7695     char *q = p;\r
7696     while (q < is->next) {\r
7697       if (*q++ == '\n') {\r
7698         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7699         p = q;\r
7700       }\r
7701     }\r
7702     \r
7703     /* Move any partial line to the start of the buffer */\r
7704     q = is->buf;\r
7705     while (p < is->next) {\r
7706       *q++ = *p++;\r
7707     }\r
7708     is->next = q;\r
7709 \r
7710     if (is->error != NO_ERROR || is->count == 0) {\r
7711       /* Notify backend of the error.  Note: If there was a partial\r
7712          line at the end, it is not flushed through. */\r
7713       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7714     }\r
7715   } else {\r
7716     /* Feed in the whole chunk of input at once */\r
7717     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7718     is->next = is->buf;\r
7719   }\r
7720 }\r
7721 \r
7722 /*---------------------------------------------------------------------------*\\r
7723  *\r
7724  *  Menu enables. Used when setting various modes.\r
7725  *\r
7726 \*---------------------------------------------------------------------------*/\r
7727 \r
7728 typedef struct {\r
7729   int item;\r
7730   int flags;\r
7731 } Enables;\r
7732 \r
7733 VOID\r
7734 GreyRevert(Boolean grey)\r
7735 { // [HGM] vari: for retracting variations in local mode\r
7736   HMENU hmenu = GetMenu(hwndMain);\r
7737   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7738   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7739 }\r
7740 \r
7741 VOID\r
7742 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7743 {\r
7744   while (enab->item > 0) {\r
7745     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7746     enab++;\r
7747   }\r
7748 }\r
7749 \r
7750 Enables gnuEnables[] = {\r
7751   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7753   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7754   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7755   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7756   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7757   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7763   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7764 \r
7765   // Needed to switch from ncp to GNU mode on Engine Load\r
7766   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7767   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7768   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7769   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7770   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7771   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7772   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7773   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7774   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7775   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7776   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7777   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7778   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7779   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7780   { -1, -1 }\r
7781 };\r
7782 \r
7783 Enables icsEnables[] = {\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_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7792   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7799   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7803   { -1, -1 }\r
7804 };\r
7805 \r
7806 #if ZIPPY\r
7807 Enables zippyEnables[] = {\r
7808   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7809   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7810   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7811   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7812   { -1, -1 }\r
7813 };\r
7814 #endif\r
7815 \r
7816 Enables ncpEnables[] = {\r
7817   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7826   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7827   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7839   { -1, -1 }\r
7840 };\r
7841 \r
7842 Enables trainingOnEnables[] = {\r
7843   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7852   { -1, -1 }\r
7853 };\r
7854 \r
7855 Enables trainingOffEnables[] = {\r
7856   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7865   { -1, -1 }\r
7866 };\r
7867 \r
7868 /* These modify either ncpEnables or gnuEnables */\r
7869 Enables cmailEnables[] = {\r
7870   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7871   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7872   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7873   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7874   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7877   { -1, -1 }\r
7878 };\r
7879 \r
7880 Enables machineThinkingEnables[] = {\r
7881   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7894 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7897   { -1, -1 }\r
7898 };\r
7899 \r
7900 Enables userThinkingEnables[] = {\r
7901   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7914 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7917   { -1, -1 }\r
7918 };\r
7919 \r
7920 /*---------------------------------------------------------------------------*\\r
7921  *\r
7922  *  Front-end interface functions exported by XBoard.\r
7923  *  Functions appear in same order as prototypes in frontend.h.\r
7924  * \r
7925 \*---------------------------------------------------------------------------*/\r
7926 VOID\r
7927 CheckMark(UINT item, int state)\r
7928 {\r
7929     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7930 }\r
7931 \r
7932 VOID\r
7933 ModeHighlight()\r
7934 {\r
7935   static UINT prevChecked = 0;\r
7936   static int prevPausing = 0;\r
7937   UINT nowChecked;\r
7938 \r
7939   if (pausing != prevPausing) {\r
7940     prevPausing = pausing;\r
7941     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7942                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7943     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7944   }\r
7945 \r
7946   switch (gameMode) {\r
7947   case BeginningOfGame:\r
7948     if (appData.icsActive)\r
7949       nowChecked = IDM_IcsClient;\r
7950     else if (appData.noChessProgram)\r
7951       nowChecked = IDM_EditGame;\r
7952     else\r
7953       nowChecked = IDM_MachineBlack;\r
7954     break;\r
7955   case MachinePlaysBlack:\r
7956     nowChecked = IDM_MachineBlack;\r
7957     break;\r
7958   case MachinePlaysWhite:\r
7959     nowChecked = IDM_MachineWhite;\r
7960     break;\r
7961   case TwoMachinesPlay:\r
7962     nowChecked = IDM_TwoMachines;\r
7963     break;\r
7964   case AnalyzeMode:\r
7965     nowChecked = IDM_AnalysisMode;\r
7966     break;\r
7967   case AnalyzeFile:\r
7968     nowChecked = IDM_AnalyzeFile;\r
7969     break;\r
7970   case EditGame:\r
7971     nowChecked = IDM_EditGame;\r
7972     break;\r
7973   case PlayFromGameFile:\r
7974     nowChecked = IDM_LoadGame;\r
7975     break;\r
7976   case EditPosition:\r
7977     nowChecked = IDM_EditPosition;\r
7978     break;\r
7979   case Training:\r
7980     nowChecked = IDM_Training;\r
7981     break;\r
7982   case IcsPlayingWhite:\r
7983   case IcsPlayingBlack:\r
7984   case IcsObserving:\r
7985   case IcsIdle:\r
7986     nowChecked = IDM_IcsClient;\r
7987     break;\r
7988   default:\r
7989   case EndOfGame:\r
7990     nowChecked = 0;\r
7991     break;\r
7992   }\r
7993   CheckMark(prevChecked, MF_UNCHECKED);\r
7994   CheckMark(nowChecked, MF_CHECKED);\r
7995   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
7996 \r
7997   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7998     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7999                           MF_BYCOMMAND|MF_ENABLED);\r
8000   } else {\r
8001     (void) EnableMenuItem(GetMenu(hwndMain), \r
8002                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8003   }\r
8004 \r
8005   prevChecked = nowChecked;\r
8006 \r
8007   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8008   if (appData.icsActive) {\r
8009        if (appData.icsEngineAnalyze) {\r
8010                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8011        } else {\r
8012                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8013        }\r
8014   }\r
8015   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8016 }\r
8017 \r
8018 VOID\r
8019 SetICSMode()\r
8020 {\r
8021   HMENU hmenu = GetMenu(hwndMain);\r
8022   SetMenuEnables(hmenu, icsEnables);\r
8023   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8024     MF_BYCOMMAND|MF_ENABLED);\r
8025 #if ZIPPY\r
8026   if (appData.zippyPlay) {\r
8027     SetMenuEnables(hmenu, zippyEnables);\r
8028     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8029          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8030           MF_BYCOMMAND|MF_ENABLED);\r
8031   }\r
8032 #endif\r
8033 }\r
8034 \r
8035 VOID\r
8036 SetGNUMode()\r
8037 {\r
8038   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8039 }\r
8040 \r
8041 VOID\r
8042 SetNCPMode()\r
8043 {\r
8044   HMENU hmenu = GetMenu(hwndMain);\r
8045   SetMenuEnables(hmenu, ncpEnables);\r
8046     DrawMenuBar(hwndMain);\r
8047 }\r
8048 \r
8049 VOID\r
8050 SetCmailMode()\r
8051 {\r
8052   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8053 }\r
8054 \r
8055 VOID \r
8056 SetTrainingModeOn()\r
8057 {\r
8058   int i;\r
8059   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8060   for (i = 0; i < N_BUTTONS; i++) {\r
8061     if (buttonDesc[i].hwnd != NULL)\r
8062       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8063   }\r
8064   CommentPopDown();\r
8065 }\r
8066 \r
8067 VOID SetTrainingModeOff()\r
8068 {\r
8069   int i;\r
8070   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8071   for (i = 0; i < N_BUTTONS; i++) {\r
8072     if (buttonDesc[i].hwnd != NULL)\r
8073       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8074   }\r
8075 }\r
8076 \r
8077 \r
8078 VOID\r
8079 SetUserThinkingEnables()\r
8080 {\r
8081   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8082 }\r
8083 \r
8084 VOID\r
8085 SetMachineThinkingEnables()\r
8086 {\r
8087   HMENU hMenu = GetMenu(hwndMain);\r
8088   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8089 \r
8090   SetMenuEnables(hMenu, machineThinkingEnables);\r
8091 \r
8092   if (gameMode == MachinePlaysBlack) {\r
8093     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8094   } else if (gameMode == MachinePlaysWhite) {\r
8095     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8096   } else if (gameMode == TwoMachinesPlay) {\r
8097     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8098   }\r
8099 }\r
8100 \r
8101 \r
8102 VOID\r
8103 DisplayTitle(char *str)\r
8104 {\r
8105   char title[MSG_SIZ], *host;\r
8106   if (str[0] != NULLCHAR) {\r
8107     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8108   } else if (appData.icsActive) {\r
8109     if (appData.icsCommPort[0] != NULLCHAR)\r
8110       host = "ICS";\r
8111     else \r
8112       host = appData.icsHost;\r
8113       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8114   } else if (appData.noChessProgram) {\r
8115     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8116   } else {\r
8117     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8118     strcat(title, ": ");\r
8119     strcat(title, first.tidy);\r
8120   }\r
8121   SetWindowText(hwndMain, title);\r
8122 }\r
8123 \r
8124 \r
8125 VOID\r
8126 DisplayMessage(char *str1, char *str2)\r
8127 {\r
8128   HDC hdc;\r
8129   HFONT oldFont;\r
8130   int remain = MESSAGE_TEXT_MAX - 1;\r
8131   int len;\r
8132 \r
8133   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8134   messageText[0] = NULLCHAR;\r
8135   if (*str1) {\r
8136     len = strlen(str1);\r
8137     if (len > remain) len = remain;\r
8138     strncpy(messageText, str1, len);\r
8139     messageText[len] = NULLCHAR;\r
8140     remain -= len;\r
8141   }\r
8142   if (*str2 && remain >= 2) {\r
8143     if (*str1) {\r
8144       strcat(messageText, "  ");\r
8145       remain -= 2;\r
8146     }\r
8147     len = strlen(str2);\r
8148     if (len > remain) len = remain;\r
8149     strncat(messageText, str2, len);\r
8150   }\r
8151   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8152   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8153 \r
8154   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8155 \r
8156   SAYMACHINEMOVE();\r
8157 \r
8158   hdc = GetDC(hwndMain);\r
8159   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8160   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8161              &messageRect, messageText, strlen(messageText), NULL);\r
8162   (void) SelectObject(hdc, oldFont);\r
8163   (void) ReleaseDC(hwndMain, hdc);\r
8164 }\r
8165 \r
8166 VOID\r
8167 DisplayError(char *str, int error)\r
8168 {\r
8169   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8170   int len;\r
8171 \r
8172   if (error == 0) {\r
8173     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8174   } else {\r
8175     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8176                         NULL, error, LANG_NEUTRAL,\r
8177                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8178     if (len > 0) {\r
8179       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8180     } else {\r
8181       ErrorMap *em = errmap;\r
8182       while (em->err != 0 && em->err != error) em++;\r
8183       if (em->err != 0) {\r
8184         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8185       } else {\r
8186         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8187       }\r
8188     }\r
8189   }\r
8190   \r
8191   ErrorPopUp(_("Error"), buf);\r
8192 }\r
8193 \r
8194 \r
8195 VOID\r
8196 DisplayMoveError(char *str)\r
8197 {\r
8198   fromX = fromY = -1;\r
8199   ClearHighlights();\r
8200   DrawPosition(FALSE, NULL);\r
8201   if (appData.popupMoveErrors) {\r
8202     ErrorPopUp(_("Error"), str);\r
8203   } else {\r
8204     DisplayMessage(str, "");\r
8205     moveErrorMessageUp = TRUE;\r
8206   }\r
8207 }\r
8208 \r
8209 VOID\r
8210 DisplayFatalError(char *str, int error, int exitStatus)\r
8211 {\r
8212   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8213   int len;\r
8214   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8215 \r
8216   if (error != 0) {\r
8217     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8218                         NULL, error, LANG_NEUTRAL,\r
8219                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8220     if (len > 0) {\r
8221       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8222     } else {\r
8223       ErrorMap *em = errmap;\r
8224       while (em->err != 0 && em->err != error) em++;\r
8225       if (em->err != 0) {\r
8226         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8227       } else {\r
8228         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8229       }\r
8230     }\r
8231     str = buf;\r
8232   }\r
8233   if (appData.debugMode) {\r
8234     fprintf(debugFP, "%s: %s\n", label, str);\r
8235   }\r
8236   if (appData.popupExitMessage) {\r
8237     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8238                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8239   }\r
8240   ExitEvent(exitStatus);\r
8241 }\r
8242 \r
8243 \r
8244 VOID\r
8245 DisplayInformation(char *str)\r
8246 {\r
8247   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8248 }\r
8249 \r
8250 \r
8251 VOID\r
8252 DisplayNote(char *str)\r
8253 {\r
8254   ErrorPopUp(_("Note"), str);\r
8255 }\r
8256 \r
8257 \r
8258 typedef struct {\r
8259   char *title, *question, *replyPrefix;\r
8260   ProcRef pr;\r
8261 } QuestionParams;\r
8262 \r
8263 LRESULT CALLBACK\r
8264 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8265 {\r
8266   static QuestionParams *qp;\r
8267   char reply[MSG_SIZ];\r
8268   int len, err;\r
8269 \r
8270   switch (message) {\r
8271   case WM_INITDIALOG:\r
8272     qp = (QuestionParams *) lParam;\r
8273     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8274     Translate(hDlg, DLG_Question);\r
8275     SetWindowText(hDlg, qp->title);\r
8276     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8277     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8278     return FALSE;\r
8279 \r
8280   case WM_COMMAND:\r
8281     switch (LOWORD(wParam)) {\r
8282     case IDOK:\r
8283       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8284       if (*reply) strcat(reply, " ");\r
8285       len = strlen(reply);\r
8286       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8287       strcat(reply, "\n");\r
8288       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8289       EndDialog(hDlg, TRUE);\r
8290       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8291       return TRUE;\r
8292     case IDCANCEL:\r
8293       EndDialog(hDlg, FALSE);\r
8294       return TRUE;\r
8295     default:\r
8296       break;\r
8297     }\r
8298     break;\r
8299   }\r
8300   return FALSE;\r
8301 }\r
8302 \r
8303 VOID\r
8304 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8305 {\r
8306     QuestionParams qp;\r
8307     FARPROC lpProc;\r
8308     \r
8309     qp.title = title;\r
8310     qp.question = question;\r
8311     qp.replyPrefix = replyPrefix;\r
8312     qp.pr = pr;\r
8313     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8314     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8315       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8316     FreeProcInstance(lpProc);\r
8317 }\r
8318 \r
8319 /* [AS] Pick FRC position */\r
8320 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8321 {\r
8322     static int * lpIndexFRC;\r
8323     BOOL index_is_ok;\r
8324     char buf[16];\r
8325 \r
8326     switch( message )\r
8327     {\r
8328     case WM_INITDIALOG:\r
8329         lpIndexFRC = (int *) lParam;\r
8330 \r
8331         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8332         Translate(hDlg, DLG_NewGameFRC);\r
8333 \r
8334         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8335         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8336         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8337         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8338 \r
8339         break;\r
8340 \r
8341     case WM_COMMAND:\r
8342         switch( LOWORD(wParam) ) {\r
8343         case IDOK:\r
8344             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8345             EndDialog( hDlg, 0 );\r
8346             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8347             return TRUE;\r
8348         case IDCANCEL:\r
8349             EndDialog( hDlg, 1 );   \r
8350             return TRUE;\r
8351         case IDC_NFG_Edit:\r
8352             if( HIWORD(wParam) == EN_CHANGE ) {\r
8353                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8354 \r
8355                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8356             }\r
8357             return TRUE;\r
8358         case IDC_NFG_Random:\r
8359           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8360             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8361             return TRUE;\r
8362         }\r
8363 \r
8364         break;\r
8365     }\r
8366 \r
8367     return FALSE;\r
8368 }\r
8369 \r
8370 int NewGameFRC()\r
8371 {\r
8372     int result;\r
8373     int index = appData.defaultFrcPosition;\r
8374     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8375 \r
8376     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8377 \r
8378     if( result == 0 ) {\r
8379         appData.defaultFrcPosition = index;\r
8380     }\r
8381 \r
8382     return result;\r
8383 }\r
8384 \r
8385 /* [AS] Game list options. Refactored by HGM */\r
8386 \r
8387 HWND gameListOptionsDialog;\r
8388 \r
8389 // low-level front-end: clear text edit / list widget\r
8390 void\r
8391 GLT_ClearList()\r
8392 {\r
8393     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8394 }\r
8395 \r
8396 // low-level front-end: clear text edit / list widget\r
8397 void\r
8398 GLT_DeSelectList()\r
8399 {\r
8400     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8401 }\r
8402 \r
8403 // low-level front-end: append line to text edit / list widget\r
8404 void\r
8405 GLT_AddToList( char *name )\r
8406 {\r
8407     if( name != 0 ) {\r
8408             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8409     }\r
8410 }\r
8411 \r
8412 // low-level front-end: get line from text edit / list widget\r
8413 Boolean\r
8414 GLT_GetFromList( int index, char *name )\r
8415 {\r
8416     if( name != 0 ) {\r
8417             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8418                 return TRUE;\r
8419     }\r
8420     return FALSE;\r
8421 }\r
8422 \r
8423 void GLT_MoveSelection( HWND hDlg, int delta )\r
8424 {\r
8425     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8426     int idx2 = idx1 + delta;\r
8427     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8428 \r
8429     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8430         char buf[128];\r
8431 \r
8432         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8433         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8434         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8435         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8436     }\r
8437 }\r
8438 \r
8439 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8440 {\r
8441     switch( message )\r
8442     {\r
8443     case WM_INITDIALOG:\r
8444         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8445         \r
8446         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8447         Translate(hDlg, DLG_GameListOptions);\r
8448 \r
8449         /* Initialize list */\r
8450         GLT_TagsToList( lpUserGLT );\r
8451 \r
8452         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8453 \r
8454         break;\r
8455 \r
8456     case WM_COMMAND:\r
8457         switch( LOWORD(wParam) ) {\r
8458         case IDOK:\r
8459             GLT_ParseList();\r
8460             EndDialog( hDlg, 0 );\r
8461             return TRUE;\r
8462         case IDCANCEL:\r
8463             EndDialog( hDlg, 1 );\r
8464             return TRUE;\r
8465 \r
8466         case IDC_GLT_Default:\r
8467             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8468             return TRUE;\r
8469 \r
8470         case IDC_GLT_Restore:\r
8471             GLT_TagsToList( appData.gameListTags );\r
8472             return TRUE;\r
8473 \r
8474         case IDC_GLT_Up:\r
8475             GLT_MoveSelection( hDlg, -1 );\r
8476             return TRUE;\r
8477 \r
8478         case IDC_GLT_Down:\r
8479             GLT_MoveSelection( hDlg, +1 );\r
8480             return TRUE;\r
8481         }\r
8482 \r
8483         break;\r
8484     }\r
8485 \r
8486     return FALSE;\r
8487 }\r
8488 \r
8489 int GameListOptions()\r
8490 {\r
8491     int result;\r
8492     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8493 \r
8494       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8495 \r
8496     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8497 \r
8498     if( result == 0 ) {\r
8499         /* [AS] Memory leak here! */\r
8500         appData.gameListTags = strdup( lpUserGLT ); \r
8501     }\r
8502 \r
8503     return result;\r
8504 }\r
8505 \r
8506 VOID\r
8507 DisplayIcsInteractionTitle(char *str)\r
8508 {\r
8509   char consoleTitle[MSG_SIZ];\r
8510 \r
8511     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8512   SetWindowText(hwndConsole, consoleTitle);\r
8513 }\r
8514 \r
8515 void\r
8516 DrawPosition(int fullRedraw, Board board)\r
8517 {\r
8518   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8519 }\r
8520 \r
8521 void NotifyFrontendLogin()\r
8522 {\r
8523         if (hwndConsole)\r
8524                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8525 }\r
8526 \r
8527 VOID\r
8528 ResetFrontEnd()\r
8529 {\r
8530   fromX = fromY = -1;\r
8531   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8532     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8533     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8534     dragInfo.lastpos = dragInfo.pos;\r
8535     dragInfo.start.x = dragInfo.start.y = -1;\r
8536     dragInfo.from = dragInfo.start;\r
8537     ReleaseCapture();\r
8538     DrawPosition(TRUE, NULL);\r
8539   }\r
8540   TagsPopDown();\r
8541 }\r
8542 \r
8543 \r
8544 VOID\r
8545 CommentPopUp(char *title, char *str)\r
8546 {\r
8547   HWND hwnd = GetActiveWindow();\r
8548   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8549   SAY(str);\r
8550   SetActiveWindow(hwnd);\r
8551 }\r
8552 \r
8553 VOID\r
8554 CommentPopDown(void)\r
8555 {\r
8556   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8557   if (commentDialog) {\r
8558     ShowWindow(commentDialog, SW_HIDE);\r
8559   }\r
8560   commentUp = FALSE;\r
8561 }\r
8562 \r
8563 VOID\r
8564 EditCommentPopUp(int index, char *title, char *str)\r
8565 {\r
8566   EitherCommentPopUp(index, title, str, TRUE);\r
8567 }\r
8568 \r
8569 \r
8570 VOID\r
8571 RingBell()\r
8572 {\r
8573   MyPlaySound(&sounds[(int)SoundMove]);\r
8574 }\r
8575 \r
8576 VOID PlayIcsWinSound()\r
8577 {\r
8578   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8579 }\r
8580 \r
8581 VOID PlayIcsLossSound()\r
8582 {\r
8583   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8584 }\r
8585 \r
8586 VOID PlayIcsDrawSound()\r
8587 {\r
8588   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8589 }\r
8590 \r
8591 VOID PlayIcsUnfinishedSound()\r
8592 {\r
8593   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8594 }\r
8595 \r
8596 VOID\r
8597 PlayAlarmSound()\r
8598 {\r
8599   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8600 }\r
8601 \r
8602 \r
8603 VOID\r
8604 EchoOn()\r
8605 {\r
8606   HWND hInput;\r
8607   consoleEcho = TRUE;\r
8608   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8609   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8610   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8611 }\r
8612 \r
8613 \r
8614 VOID\r
8615 EchoOff()\r
8616 {\r
8617   CHARFORMAT cf;\r
8618   HWND hInput;\r
8619   consoleEcho = FALSE;\r
8620   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8621   /* This works OK: set text and background both to the same color */\r
8622   cf = consoleCF;\r
8623   cf.crTextColor = COLOR_ECHOOFF;\r
8624   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8625   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8626 }\r
8627 \r
8628 /* No Raw()...? */\r
8629 \r
8630 void Colorize(ColorClass cc, int continuation)\r
8631 {\r
8632   currentColorClass = cc;\r
8633   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8634   consoleCF.crTextColor = textAttribs[cc].color;\r
8635   consoleCF.dwEffects = textAttribs[cc].effects;\r
8636   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8637 }\r
8638 \r
8639 char *\r
8640 UserName()\r
8641 {\r
8642   static char buf[MSG_SIZ];\r
8643   DWORD bufsiz = MSG_SIZ;\r
8644 \r
8645   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8646         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8647   }\r
8648   if (!GetUserName(buf, &bufsiz)) {\r
8649     /*DisplayError("Error getting user name", GetLastError());*/\r
8650     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8651   }\r
8652   return buf;\r
8653 }\r
8654 \r
8655 char *\r
8656 HostName()\r
8657 {\r
8658   static char buf[MSG_SIZ];\r
8659   DWORD bufsiz = MSG_SIZ;\r
8660 \r
8661   if (!GetComputerName(buf, &bufsiz)) {\r
8662     /*DisplayError("Error getting host name", GetLastError());*/\r
8663     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8664   }\r
8665   return buf;\r
8666 }\r
8667 \r
8668 \r
8669 int\r
8670 ClockTimerRunning()\r
8671 {\r
8672   return clockTimerEvent != 0;\r
8673 }\r
8674 \r
8675 int\r
8676 StopClockTimer()\r
8677 {\r
8678   if (clockTimerEvent == 0) return FALSE;\r
8679   KillTimer(hwndMain, clockTimerEvent);\r
8680   clockTimerEvent = 0;\r
8681   return TRUE;\r
8682 }\r
8683 \r
8684 void\r
8685 StartClockTimer(long millisec)\r
8686 {\r
8687   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8688                              (UINT) millisec, NULL);\r
8689 }\r
8690 \r
8691 void\r
8692 DisplayWhiteClock(long timeRemaining, int highlight)\r
8693 {\r
8694   HDC hdc;\r
8695   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8696 \r
8697   if(appData.noGUI) return;\r
8698   hdc = GetDC(hwndMain);\r
8699   if (!IsIconic(hwndMain)) {\r
8700     DisplayAClock(hdc, timeRemaining, highlight, \r
8701                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8702   }\r
8703   if (highlight && iconCurrent == iconBlack) {\r
8704     iconCurrent = iconWhite;\r
8705     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8706     if (IsIconic(hwndMain)) {\r
8707       DrawIcon(hdc, 2, 2, iconCurrent);\r
8708     }\r
8709   }\r
8710   (void) ReleaseDC(hwndMain, hdc);\r
8711   if (hwndConsole)\r
8712     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8713 }\r
8714 \r
8715 void\r
8716 DisplayBlackClock(long timeRemaining, int highlight)\r
8717 {\r
8718   HDC hdc;\r
8719   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8720 \r
8721   if(appData.noGUI) return;\r
8722   hdc = GetDC(hwndMain);\r
8723   if (!IsIconic(hwndMain)) {\r
8724     DisplayAClock(hdc, timeRemaining, highlight, \r
8725                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8726   }\r
8727   if (highlight && iconCurrent == iconWhite) {\r
8728     iconCurrent = iconBlack;\r
8729     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8730     if (IsIconic(hwndMain)) {\r
8731       DrawIcon(hdc, 2, 2, iconCurrent);\r
8732     }\r
8733   }\r
8734   (void) ReleaseDC(hwndMain, hdc);\r
8735   if (hwndConsole)\r
8736     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8737 }\r
8738 \r
8739 \r
8740 int\r
8741 LoadGameTimerRunning()\r
8742 {\r
8743   return loadGameTimerEvent != 0;\r
8744 }\r
8745 \r
8746 int\r
8747 StopLoadGameTimer()\r
8748 {\r
8749   if (loadGameTimerEvent == 0) return FALSE;\r
8750   KillTimer(hwndMain, loadGameTimerEvent);\r
8751   loadGameTimerEvent = 0;\r
8752   return TRUE;\r
8753 }\r
8754 \r
8755 void\r
8756 StartLoadGameTimer(long millisec)\r
8757 {\r
8758   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8759                                 (UINT) millisec, NULL);\r
8760 }\r
8761 \r
8762 void\r
8763 AutoSaveGame()\r
8764 {\r
8765   char *defName;\r
8766   FILE *f;\r
8767   char fileTitle[MSG_SIZ];\r
8768 \r
8769   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8770   f = OpenFileDialog(hwndMain, "a", defName,\r
8771                      appData.oldSaveStyle ? "gam" : "pgn",\r
8772                      GAME_FILT, \r
8773                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8774   if (f != NULL) {\r
8775     SaveGame(f, 0, "");\r
8776     fclose(f);\r
8777   }\r
8778 }\r
8779 \r
8780 \r
8781 void\r
8782 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8783 {\r
8784   if (delayedTimerEvent != 0) {\r
8785     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8786       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8787     }\r
8788     KillTimer(hwndMain, delayedTimerEvent);\r
8789     delayedTimerEvent = 0;\r
8790     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8791     delayedTimerCallback();\r
8792   }\r
8793   delayedTimerCallback = cb;\r
8794   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8795                                 (UINT) millisec, NULL);\r
8796 }\r
8797 \r
8798 DelayedEventCallback\r
8799 GetDelayedEvent()\r
8800 {\r
8801   if (delayedTimerEvent) {\r
8802     return delayedTimerCallback;\r
8803   } else {\r
8804     return NULL;\r
8805   }\r
8806 }\r
8807 \r
8808 void\r
8809 CancelDelayedEvent()\r
8810 {\r
8811   if (delayedTimerEvent) {\r
8812     KillTimer(hwndMain, delayedTimerEvent);\r
8813     delayedTimerEvent = 0;\r
8814   }\r
8815 }\r
8816 \r
8817 DWORD GetWin32Priority(int nice)\r
8818 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8819 /*\r
8820 REALTIME_PRIORITY_CLASS     0x00000100\r
8821 HIGH_PRIORITY_CLASS         0x00000080\r
8822 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8823 NORMAL_PRIORITY_CLASS       0x00000020\r
8824 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8825 IDLE_PRIORITY_CLASS         0x00000040\r
8826 */\r
8827         if (nice < -15) return 0x00000080;\r
8828         if (nice < 0)   return 0x00008000;\r
8829         if (nice == 0)  return 0x00000020;\r
8830         if (nice < 15)  return 0x00004000;\r
8831         return 0x00000040;\r
8832 }\r
8833 \r
8834 /* Start a child process running the given program.\r
8835    The process's standard output can be read from "from", and its\r
8836    standard input can be written to "to".\r
8837    Exit with fatal error if anything goes wrong.\r
8838    Returns an opaque pointer that can be used to destroy the process\r
8839    later.\r
8840 */\r
8841 int\r
8842 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8843 {\r
8844 #define BUFSIZE 4096\r
8845 \r
8846   HANDLE hChildStdinRd, hChildStdinWr,\r
8847     hChildStdoutRd, hChildStdoutWr;\r
8848   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8849   SECURITY_ATTRIBUTES saAttr;\r
8850   BOOL fSuccess;\r
8851   PROCESS_INFORMATION piProcInfo;\r
8852   STARTUPINFO siStartInfo;\r
8853   ChildProc *cp;\r
8854   char buf[MSG_SIZ];\r
8855   DWORD err;\r
8856 \r
8857   if (appData.debugMode) {\r
8858     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8859   }\r
8860 \r
8861   *pr = NoProc;\r
8862 \r
8863   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8864   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8865   saAttr.bInheritHandle = TRUE;\r
8866   saAttr.lpSecurityDescriptor = NULL;\r
8867 \r
8868   /*\r
8869    * The steps for redirecting child's STDOUT:\r
8870    *     1. Create anonymous pipe to be STDOUT for child.\r
8871    *     2. Create a noninheritable duplicate of read handle,\r
8872    *         and close the inheritable read handle.\r
8873    */\r
8874 \r
8875   /* Create a pipe for the child's STDOUT. */\r
8876   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8877     return GetLastError();\r
8878   }\r
8879 \r
8880   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8881   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8882                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8883                              FALSE,     /* not inherited */\r
8884                              DUPLICATE_SAME_ACCESS);\r
8885   if (! fSuccess) {\r
8886     return GetLastError();\r
8887   }\r
8888   CloseHandle(hChildStdoutRd);\r
8889 \r
8890   /*\r
8891    * The steps for redirecting child's STDIN:\r
8892    *     1. Create anonymous pipe to be STDIN for child.\r
8893    *     2. Create a noninheritable duplicate of write handle,\r
8894    *         and close the inheritable write handle.\r
8895    */\r
8896 \r
8897   /* Create a pipe for the child's STDIN. */\r
8898   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8899     return GetLastError();\r
8900   }\r
8901 \r
8902   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8903   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8904                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8905                              FALSE,     /* not inherited */\r
8906                              DUPLICATE_SAME_ACCESS);\r
8907   if (! fSuccess) {\r
8908     return GetLastError();\r
8909   }\r
8910   CloseHandle(hChildStdinWr);\r
8911 \r
8912   /* Arrange to (1) look in dir for the child .exe file, and\r
8913    * (2) have dir be the child's working directory.  Interpret\r
8914    * dir relative to the directory WinBoard loaded from. */\r
8915   GetCurrentDirectory(MSG_SIZ, buf);\r
8916   SetCurrentDirectory(installDir);\r
8917   // kludgey way to update logos in tourney, as long as back-end can't do it\r
8918   if(!strcmp(cmdLine, first.program)) LoadLogo(&first, 0); else\r
8919   if(!strcmp(cmdLine, second.program)) LoadLogo(&second, 1);\r
8920   SetCurrentDirectory(dir);\r
8921 \r
8922   /* Now create the child process. */\r
8923 \r
8924   siStartInfo.cb = sizeof(STARTUPINFO);\r
8925   siStartInfo.lpReserved = NULL;\r
8926   siStartInfo.lpDesktop = NULL;\r
8927   siStartInfo.lpTitle = NULL;\r
8928   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8929   siStartInfo.cbReserved2 = 0;\r
8930   siStartInfo.lpReserved2 = NULL;\r
8931   siStartInfo.hStdInput = hChildStdinRd;\r
8932   siStartInfo.hStdOutput = hChildStdoutWr;\r
8933   siStartInfo.hStdError = hChildStdoutWr;\r
8934 \r
8935   fSuccess = CreateProcess(NULL,\r
8936                            cmdLine,        /* command line */\r
8937                            NULL,           /* process security attributes */\r
8938                            NULL,           /* primary thread security attrs */\r
8939                            TRUE,           /* handles are inherited */\r
8940                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8941                            NULL,           /* use parent's environment */\r
8942                            NULL,\r
8943                            &siStartInfo, /* STARTUPINFO pointer */\r
8944                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8945 \r
8946   err = GetLastError();\r
8947   SetCurrentDirectory(buf); /* return to prev directory */\r
8948   if (! fSuccess) {\r
8949     return err;\r
8950   }\r
8951 \r
8952   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8953     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8954     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8955   }\r
8956 \r
8957   /* Close the handles we don't need in the parent */\r
8958   CloseHandle(piProcInfo.hThread);\r
8959   CloseHandle(hChildStdinRd);\r
8960   CloseHandle(hChildStdoutWr);\r
8961 \r
8962   /* Prepare return value */\r
8963   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8964   cp->kind = CPReal;\r
8965   cp->hProcess = piProcInfo.hProcess;\r
8966   cp->pid = piProcInfo.dwProcessId;\r
8967   cp->hFrom = hChildStdoutRdDup;\r
8968   cp->hTo = hChildStdinWrDup;\r
8969 \r
8970   *pr = (void *) cp;\r
8971 \r
8972   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8973      2000 where engines sometimes don't see the initial command(s)\r
8974      from WinBoard and hang.  I don't understand how that can happen,\r
8975      but the Sleep is harmless, so I've put it in.  Others have also\r
8976      reported what may be the same problem, so hopefully this will fix\r
8977      it for them too.  */\r
8978   Sleep(500);\r
8979 \r
8980   return NO_ERROR;\r
8981 }\r
8982 \r
8983 \r
8984 void\r
8985 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8986 {\r
8987   ChildProc *cp; int result;\r
8988 \r
8989   cp = (ChildProc *) pr;\r
8990   if (cp == NULL) return;\r
8991 \r
8992   switch (cp->kind) {\r
8993   case CPReal:\r
8994     /* TerminateProcess is considered harmful, so... */\r
8995     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8996     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8997     /* The following doesn't work because the chess program\r
8998        doesn't "have the same console" as WinBoard.  Maybe\r
8999        we could arrange for this even though neither WinBoard\r
9000        nor the chess program uses a console for stdio? */\r
9001     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9002 \r
9003     /* [AS] Special termination modes for misbehaving programs... */\r
9004     if( signal == 9 ) { \r
9005         result = TerminateProcess( cp->hProcess, 0 );\r
9006 \r
9007         if ( appData.debugMode) {\r
9008             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9009         }\r
9010     }\r
9011     else if( signal == 10 ) {\r
9012         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9013 \r
9014         if( dw != WAIT_OBJECT_0 ) {\r
9015             result = TerminateProcess( cp->hProcess, 0 );\r
9016 \r
9017             if ( appData.debugMode) {\r
9018                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9019             }\r
9020 \r
9021         }\r
9022     }\r
9023 \r
9024     CloseHandle(cp->hProcess);\r
9025     break;\r
9026 \r
9027   case CPComm:\r
9028     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9029     break;\r
9030 \r
9031   case CPSock:\r
9032     closesocket(cp->sock);\r
9033     WSACleanup();\r
9034     break;\r
9035 \r
9036   case CPRcmd:\r
9037     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9038     closesocket(cp->sock);\r
9039     closesocket(cp->sock2);\r
9040     WSACleanup();\r
9041     break;\r
9042   }\r
9043   free(cp);\r
9044 }\r
9045 \r
9046 void\r
9047 InterruptChildProcess(ProcRef pr)\r
9048 {\r
9049   ChildProc *cp;\r
9050 \r
9051   cp = (ChildProc *) pr;\r
9052   if (cp == NULL) return;\r
9053   switch (cp->kind) {\r
9054   case CPReal:\r
9055     /* The following doesn't work because the chess program\r
9056        doesn't "have the same console" as WinBoard.  Maybe\r
9057        we could arrange for this even though neither WinBoard\r
9058        nor the chess program uses a console for stdio */\r
9059     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9060     break;\r
9061 \r
9062   case CPComm:\r
9063   case CPSock:\r
9064     /* Can't interrupt */\r
9065     break;\r
9066 \r
9067   case CPRcmd:\r
9068     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9069     break;\r
9070   }\r
9071 }\r
9072 \r
9073 \r
9074 int\r
9075 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9076 {\r
9077   char cmdLine[MSG_SIZ];\r
9078 \r
9079   if (port[0] == NULLCHAR) {\r
9080     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9081   } else {\r
9082     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9083   }\r
9084   return StartChildProcess(cmdLine, "", pr);\r
9085 }\r
9086 \r
9087 \r
9088 /* Code to open TCP sockets */\r
9089 \r
9090 int\r
9091 OpenTCP(char *host, char *port, ProcRef *pr)\r
9092 {\r
9093   ChildProc *cp;\r
9094   int err;\r
9095   SOCKET s;\r
9096   struct sockaddr_in sa, mysa;\r
9097   struct hostent FAR *hp;\r
9098   unsigned short uport;\r
9099   WORD wVersionRequested;\r
9100   WSADATA wsaData;\r
9101 \r
9102   /* Initialize socket DLL */\r
9103   wVersionRequested = MAKEWORD(1, 1);\r
9104   err = WSAStartup(wVersionRequested, &wsaData);\r
9105   if (err != 0) return err;\r
9106 \r
9107   /* Make socket */\r
9108   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9109     err = WSAGetLastError();\r
9110     WSACleanup();\r
9111     return err;\r
9112   }\r
9113 \r
9114   /* Bind local address using (mostly) don't-care values.\r
9115    */\r
9116   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9117   mysa.sin_family = AF_INET;\r
9118   mysa.sin_addr.s_addr = INADDR_ANY;\r
9119   uport = (unsigned short) 0;\r
9120   mysa.sin_port = htons(uport);\r
9121   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9122       == SOCKET_ERROR) {\r
9123     err = WSAGetLastError();\r
9124     WSACleanup();\r
9125     return err;\r
9126   }\r
9127 \r
9128   /* Resolve remote host name */\r
9129   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9130   if (!(hp = gethostbyname(host))) {\r
9131     unsigned int b0, b1, b2, b3;\r
9132 \r
9133     err = WSAGetLastError();\r
9134 \r
9135     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9136       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9137       hp->h_addrtype = AF_INET;\r
9138       hp->h_length = 4;\r
9139       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9140       hp->h_addr_list[0] = (char *) malloc(4);\r
9141       hp->h_addr_list[0][0] = (char) b0;\r
9142       hp->h_addr_list[0][1] = (char) b1;\r
9143       hp->h_addr_list[0][2] = (char) b2;\r
9144       hp->h_addr_list[0][3] = (char) b3;\r
9145     } else {\r
9146       WSACleanup();\r
9147       return err;\r
9148     }\r
9149   }\r
9150   sa.sin_family = hp->h_addrtype;\r
9151   uport = (unsigned short) atoi(port);\r
9152   sa.sin_port = htons(uport);\r
9153   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9154 \r
9155   /* Make connection */\r
9156   if (connect(s, (struct sockaddr *) &sa,\r
9157               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9158     err = WSAGetLastError();\r
9159     WSACleanup();\r
9160     return err;\r
9161   }\r
9162 \r
9163   /* Prepare return value */\r
9164   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9165   cp->kind = CPSock;\r
9166   cp->sock = s;\r
9167   *pr = (ProcRef *) cp;\r
9168 \r
9169   return NO_ERROR;\r
9170 }\r
9171 \r
9172 int\r
9173 OpenCommPort(char *name, ProcRef *pr)\r
9174 {\r
9175   HANDLE h;\r
9176   COMMTIMEOUTS ct;\r
9177   ChildProc *cp;\r
9178   char fullname[MSG_SIZ];\r
9179 \r
9180   if (*name != '\\')\r
9181     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9182   else\r
9183     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9184 \r
9185   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9186                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9187   if (h == (HANDLE) -1) {\r
9188     return GetLastError();\r
9189   }\r
9190   hCommPort = h;\r
9191 \r
9192   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9193 \r
9194   /* Accumulate characters until a 100ms pause, then parse */\r
9195   ct.ReadIntervalTimeout = 100;\r
9196   ct.ReadTotalTimeoutMultiplier = 0;\r
9197   ct.ReadTotalTimeoutConstant = 0;\r
9198   ct.WriteTotalTimeoutMultiplier = 0;\r
9199   ct.WriteTotalTimeoutConstant = 0;\r
9200   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9201 \r
9202   /* Prepare return value */\r
9203   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9204   cp->kind = CPComm;\r
9205   cp->hFrom = h;\r
9206   cp->hTo = h;\r
9207   *pr = (ProcRef *) cp;\r
9208 \r
9209   return NO_ERROR;\r
9210 }\r
9211 \r
9212 int\r
9213 OpenLoopback(ProcRef *pr)\r
9214 {\r
9215   DisplayFatalError(_("Not implemented"), 0, 1);\r
9216   return NO_ERROR;\r
9217 }\r
9218 \r
9219 \r
9220 int\r
9221 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9222 {\r
9223   ChildProc *cp;\r
9224   int err;\r
9225   SOCKET s, s2, s3;\r
9226   struct sockaddr_in sa, mysa;\r
9227   struct hostent FAR *hp;\r
9228   unsigned short uport;\r
9229   WORD wVersionRequested;\r
9230   WSADATA wsaData;\r
9231   int fromPort;\r
9232   char stderrPortStr[MSG_SIZ];\r
9233 \r
9234   /* Initialize socket DLL */\r
9235   wVersionRequested = MAKEWORD(1, 1);\r
9236   err = WSAStartup(wVersionRequested, &wsaData);\r
9237   if (err != 0) return err;\r
9238 \r
9239   /* Resolve remote host name */\r
9240   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9241   if (!(hp = gethostbyname(host))) {\r
9242     unsigned int b0, b1, b2, b3;\r
9243 \r
9244     err = WSAGetLastError();\r
9245 \r
9246     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9247       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9248       hp->h_addrtype = AF_INET;\r
9249       hp->h_length = 4;\r
9250       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9251       hp->h_addr_list[0] = (char *) malloc(4);\r
9252       hp->h_addr_list[0][0] = (char) b0;\r
9253       hp->h_addr_list[0][1] = (char) b1;\r
9254       hp->h_addr_list[0][2] = (char) b2;\r
9255       hp->h_addr_list[0][3] = (char) b3;\r
9256     } else {\r
9257       WSACleanup();\r
9258       return err;\r
9259     }\r
9260   }\r
9261   sa.sin_family = hp->h_addrtype;\r
9262   uport = (unsigned short) 514;\r
9263   sa.sin_port = htons(uport);\r
9264   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9265 \r
9266   /* Bind local socket to unused "privileged" port address\r
9267    */\r
9268   s = INVALID_SOCKET;\r
9269   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9270   mysa.sin_family = AF_INET;\r
9271   mysa.sin_addr.s_addr = INADDR_ANY;\r
9272   for (fromPort = 1023;; fromPort--) {\r
9273     if (fromPort < 0) {\r
9274       WSACleanup();\r
9275       return WSAEADDRINUSE;\r
9276     }\r
9277     if (s == INVALID_SOCKET) {\r
9278       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9279         err = WSAGetLastError();\r
9280         WSACleanup();\r
9281         return err;\r
9282       }\r
9283     }\r
9284     uport = (unsigned short) fromPort;\r
9285     mysa.sin_port = htons(uport);\r
9286     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9287         == SOCKET_ERROR) {\r
9288       err = WSAGetLastError();\r
9289       if (err == WSAEADDRINUSE) continue;\r
9290       WSACleanup();\r
9291       return err;\r
9292     }\r
9293     if (connect(s, (struct sockaddr *) &sa,\r
9294       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9295       err = WSAGetLastError();\r
9296       if (err == WSAEADDRINUSE) {\r
9297         closesocket(s);\r
9298         s = -1;\r
9299         continue;\r
9300       }\r
9301       WSACleanup();\r
9302       return err;\r
9303     }\r
9304     break;\r
9305   }\r
9306 \r
9307   /* Bind stderr local socket to unused "privileged" port address\r
9308    */\r
9309   s2 = INVALID_SOCKET;\r
9310   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9311   mysa.sin_family = AF_INET;\r
9312   mysa.sin_addr.s_addr = INADDR_ANY;\r
9313   for (fromPort = 1023;; fromPort--) {\r
9314     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9315     if (fromPort < 0) {\r
9316       (void) closesocket(s);\r
9317       WSACleanup();\r
9318       return WSAEADDRINUSE;\r
9319     }\r
9320     if (s2 == INVALID_SOCKET) {\r
9321       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9322         err = WSAGetLastError();\r
9323         closesocket(s);\r
9324         WSACleanup();\r
9325         return err;\r
9326       }\r
9327     }\r
9328     uport = (unsigned short) fromPort;\r
9329     mysa.sin_port = htons(uport);\r
9330     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9331         == SOCKET_ERROR) {\r
9332       err = WSAGetLastError();\r
9333       if (err == WSAEADDRINUSE) continue;\r
9334       (void) closesocket(s);\r
9335       WSACleanup();\r
9336       return err;\r
9337     }\r
9338     if (listen(s2, 1) == SOCKET_ERROR) {\r
9339       err = WSAGetLastError();\r
9340       if (err == WSAEADDRINUSE) {\r
9341         closesocket(s2);\r
9342         s2 = INVALID_SOCKET;\r
9343         continue;\r
9344       }\r
9345       (void) closesocket(s);\r
9346       (void) closesocket(s2);\r
9347       WSACleanup();\r
9348       return err;\r
9349     }\r
9350     break;\r
9351   }\r
9352   prevStderrPort = fromPort; // remember port used\r
9353   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9354 \r
9355   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9356     err = WSAGetLastError();\r
9357     (void) closesocket(s);\r
9358     (void) closesocket(s2);\r
9359     WSACleanup();\r
9360     return err;\r
9361   }\r
9362 \r
9363   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9364     err = WSAGetLastError();\r
9365     (void) closesocket(s);\r
9366     (void) closesocket(s2);\r
9367     WSACleanup();\r
9368     return err;\r
9369   }\r
9370   if (*user == NULLCHAR) user = UserName();\r
9371   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9372     err = WSAGetLastError();\r
9373     (void) closesocket(s);\r
9374     (void) closesocket(s2);\r
9375     WSACleanup();\r
9376     return err;\r
9377   }\r
9378   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9379     err = WSAGetLastError();\r
9380     (void) closesocket(s);\r
9381     (void) closesocket(s2);\r
9382     WSACleanup();\r
9383     return err;\r
9384   }\r
9385 \r
9386   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9387     err = WSAGetLastError();\r
9388     (void) closesocket(s);\r
9389     (void) closesocket(s2);\r
9390     WSACleanup();\r
9391     return err;\r
9392   }\r
9393   (void) closesocket(s2);  /* Stop listening */\r
9394 \r
9395   /* Prepare return value */\r
9396   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9397   cp->kind = CPRcmd;\r
9398   cp->sock = s;\r
9399   cp->sock2 = s3;\r
9400   *pr = (ProcRef *) cp;\r
9401 \r
9402   return NO_ERROR;\r
9403 }\r
9404 \r
9405 \r
9406 InputSourceRef\r
9407 AddInputSource(ProcRef pr, int lineByLine,\r
9408                InputCallback func, VOIDSTAR closure)\r
9409 {\r
9410   InputSource *is, *is2 = NULL;\r
9411   ChildProc *cp = (ChildProc *) pr;\r
9412 \r
9413   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9414   is->lineByLine = lineByLine;\r
9415   is->func = func;\r
9416   is->closure = closure;\r
9417   is->second = NULL;\r
9418   is->next = is->buf;\r
9419   if (pr == NoProc) {\r
9420     is->kind = CPReal;\r
9421     consoleInputSource = is;\r
9422   } else {\r
9423     is->kind = cp->kind;\r
9424     /* \r
9425         [AS] Try to avoid a race condition if the thread is given control too early:\r
9426         we create all threads suspended so that the is->hThread variable can be\r
9427         safely assigned, then let the threads start with ResumeThread.\r
9428     */\r
9429     switch (cp->kind) {\r
9430     case CPReal:\r
9431       is->hFile = cp->hFrom;\r
9432       cp->hFrom = NULL; /* now owned by InputThread */\r
9433       is->hThread =\r
9434         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9435                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9436       break;\r
9437 \r
9438     case CPComm:\r
9439       is->hFile = cp->hFrom;\r
9440       cp->hFrom = NULL; /* now owned by InputThread */\r
9441       is->hThread =\r
9442         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9443                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9444       break;\r
9445 \r
9446     case CPSock:\r
9447       is->sock = cp->sock;\r
9448       is->hThread =\r
9449         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9450                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9451       break;\r
9452 \r
9453     case CPRcmd:\r
9454       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9455       *is2 = *is;\r
9456       is->sock = cp->sock;\r
9457       is->second = is2;\r
9458       is2->sock = cp->sock2;\r
9459       is2->second = is2;\r
9460       is->hThread =\r
9461         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9462                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9463       is2->hThread =\r
9464         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9465                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9466       break;\r
9467     }\r
9468 \r
9469     if( is->hThread != NULL ) {\r
9470         ResumeThread( is->hThread );\r
9471     }\r
9472 \r
9473     if( is2 != NULL && is2->hThread != NULL ) {\r
9474         ResumeThread( is2->hThread );\r
9475     }\r
9476   }\r
9477 \r
9478   return (InputSourceRef) is;\r
9479 }\r
9480 \r
9481 void\r
9482 RemoveInputSource(InputSourceRef isr)\r
9483 {\r
9484   InputSource *is;\r
9485 \r
9486   is = (InputSource *) isr;\r
9487   is->hThread = NULL;  /* tell thread to stop */\r
9488   CloseHandle(is->hThread);\r
9489   if (is->second != NULL) {\r
9490     is->second->hThread = NULL;\r
9491     CloseHandle(is->second->hThread);\r
9492   }\r
9493 }\r
9494 \r
9495 int no_wrap(char *message, int count)\r
9496 {\r
9497     ConsoleOutput(message, count, FALSE);\r
9498     return count;\r
9499 }\r
9500 \r
9501 int\r
9502 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9503 {\r
9504   DWORD dOutCount;\r
9505   int outCount = SOCKET_ERROR;\r
9506   ChildProc *cp = (ChildProc *) pr;\r
9507   static OVERLAPPED ovl;\r
9508   static int line = 0;\r
9509 \r
9510   if (pr == NoProc)\r
9511   {\r
9512     if (appData.noJoin || !appData.useInternalWrap)\r
9513       return no_wrap(message, count);\r
9514     else\r
9515     {\r
9516       int width = get_term_width();\r
9517       int len = wrap(NULL, message, count, width, &line);\r
9518       char *msg = malloc(len);\r
9519       int dbgchk;\r
9520 \r
9521       if (!msg)\r
9522         return no_wrap(message, count);\r
9523       else\r
9524       {\r
9525         dbgchk = wrap(msg, message, count, width, &line);\r
9526         if (dbgchk != len && appData.debugMode)\r
9527             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9528         ConsoleOutput(msg, len, FALSE);\r
9529         free(msg);\r
9530         return len;\r
9531       }\r
9532     }\r
9533   }\r
9534 \r
9535   if (ovl.hEvent == NULL) {\r
9536     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9537   }\r
9538   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9539 \r
9540   switch (cp->kind) {\r
9541   case CPSock:\r
9542   case CPRcmd:\r
9543     outCount = send(cp->sock, message, count, 0);\r
9544     if (outCount == SOCKET_ERROR) {\r
9545       *outError = WSAGetLastError();\r
9546     } else {\r
9547       *outError = NO_ERROR;\r
9548     }\r
9549     break;\r
9550 \r
9551   case CPReal:\r
9552     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9553                   &dOutCount, NULL)) {\r
9554       *outError = NO_ERROR;\r
9555       outCount = (int) dOutCount;\r
9556     } else {\r
9557       *outError = GetLastError();\r
9558     }\r
9559     break;\r
9560 \r
9561   case CPComm:\r
9562     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9563                             &dOutCount, &ovl);\r
9564     if (*outError == NO_ERROR) {\r
9565       outCount = (int) dOutCount;\r
9566     }\r
9567     break;\r
9568   }\r
9569   return outCount;\r
9570 }\r
9571 \r
9572 int\r
9573 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9574                        long msdelay)\r
9575 {\r
9576   /* Ignore delay, not implemented for WinBoard */\r
9577   return OutputToProcess(pr, message, count, outError);\r
9578 }\r
9579 \r
9580 \r
9581 void\r
9582 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9583                         char *buf, int count, int error)\r
9584 {\r
9585   DisplayFatalError(_("Not implemented"), 0, 1);\r
9586 }\r
9587 \r
9588 /* see wgamelist.c for Game List functions */\r
9589 /* see wedittags.c for Edit Tags functions */\r
9590 \r
9591 \r
9592 VOID\r
9593 ICSInitScript()\r
9594 {\r
9595   FILE *f;\r
9596   char buf[MSG_SIZ];\r
9597   char *dummy;\r
9598 \r
9599   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9600     f = fopen(buf, "r");\r
9601     if (f != NULL) {\r
9602       ProcessICSInitScript(f);\r
9603       fclose(f);\r
9604     }\r
9605   }\r
9606 }\r
9607 \r
9608 \r
9609 VOID\r
9610 StartAnalysisClock()\r
9611 {\r
9612   if (analysisTimerEvent) return;\r
9613   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9614                                         (UINT) 2000, NULL);\r
9615 }\r
9616 \r
9617 VOID\r
9618 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9619 {\r
9620   highlightInfo.sq[0].x = fromX;\r
9621   highlightInfo.sq[0].y = fromY;\r
9622   highlightInfo.sq[1].x = toX;\r
9623   highlightInfo.sq[1].y = toY;\r
9624 }\r
9625 \r
9626 VOID\r
9627 ClearHighlights()\r
9628 {\r
9629   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9630     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9631 }\r
9632 \r
9633 VOID\r
9634 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9635 {\r
9636   premoveHighlightInfo.sq[0].x = fromX;\r
9637   premoveHighlightInfo.sq[0].y = fromY;\r
9638   premoveHighlightInfo.sq[1].x = toX;\r
9639   premoveHighlightInfo.sq[1].y = toY;\r
9640 }\r
9641 \r
9642 VOID\r
9643 ClearPremoveHighlights()\r
9644 {\r
9645   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9646     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9647 }\r
9648 \r
9649 VOID\r
9650 ShutDownFrontEnd()\r
9651 {\r
9652   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9653   DeleteClipboardTempFiles();\r
9654 }\r
9655 \r
9656 void\r
9657 BoardToTop()\r
9658 {\r
9659     if (IsIconic(hwndMain))\r
9660       ShowWindow(hwndMain, SW_RESTORE);\r
9661 \r
9662     SetActiveWindow(hwndMain);\r
9663 }\r
9664 \r
9665 /*\r
9666  * Prototypes for animation support routines\r
9667  */\r
9668 static void ScreenSquare(int column, int row, POINT * pt);\r
9669 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9670      POINT frames[], int * nFrames);\r
9671 \r
9672 \r
9673 #define kFactor 4\r
9674 \r
9675 void\r
9676 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9677 {       // [HGM] atomic: animate blast wave\r
9678         int i;\r
9679 \r
9680         explodeInfo.fromX = fromX;\r
9681         explodeInfo.fromY = fromY;\r
9682         explodeInfo.toX = toX;\r
9683         explodeInfo.toY = toY;\r
9684         for(i=1; i<4*kFactor; i++) {\r
9685             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9686             DrawPosition(FALSE, board);\r
9687             Sleep(appData.animSpeed);\r
9688         }\r
9689         explodeInfo.radius = 0;\r
9690         DrawPosition(TRUE, board);\r
9691 }\r
9692 \r
9693 void\r
9694 AnimateMove(board, fromX, fromY, toX, toY)\r
9695      Board board;\r
9696      int fromX;\r
9697      int fromY;\r
9698      int toX;\r
9699      int toY;\r
9700 {\r
9701   ChessSquare piece;\r
9702   POINT start, finish, mid;\r
9703   POINT frames[kFactor * 2 + 1];\r
9704   int nFrames, n;\r
9705 \r
9706   if (!appData.animate) return;\r
9707   if (doingSizing) return;\r
9708   if (fromY < 0 || fromX < 0) return;\r
9709   piece = board[fromY][fromX];\r
9710   if (piece >= EmptySquare) return;\r
9711 \r
9712   ScreenSquare(fromX, fromY, &start);\r
9713   ScreenSquare(toX, toY, &finish);\r
9714 \r
9715   /* All moves except knight jumps move in straight line */\r
9716   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9717     mid.x = start.x + (finish.x - start.x) / 2;\r
9718     mid.y = start.y + (finish.y - start.y) / 2;\r
9719   } else {\r
9720     /* Knight: make straight movement then diagonal */\r
9721     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9722        mid.x = start.x + (finish.x - start.x) / 2;\r
9723        mid.y = start.y;\r
9724      } else {\r
9725        mid.x = start.x;\r
9726        mid.y = start.y + (finish.y - start.y) / 2;\r
9727      }\r
9728   }\r
9729   \r
9730   /* Don't use as many frames for very short moves */\r
9731   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9732     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9733   else\r
9734     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9735 \r
9736   animInfo.from.x = fromX;\r
9737   animInfo.from.y = fromY;\r
9738   animInfo.to.x = toX;\r
9739   animInfo.to.y = toY;\r
9740   animInfo.lastpos = start;\r
9741   animInfo.piece = piece;\r
9742   for (n = 0; n < nFrames; n++) {\r
9743     animInfo.pos = frames[n];\r
9744     DrawPosition(FALSE, NULL);\r
9745     animInfo.lastpos = animInfo.pos;\r
9746     Sleep(appData.animSpeed);\r
9747   }\r
9748   animInfo.pos = finish;\r
9749   DrawPosition(FALSE, NULL);\r
9750   animInfo.piece = EmptySquare;\r
9751   Explode(board, fromX, fromY, toX, toY);\r
9752 }\r
9753 \r
9754 /*      Convert board position to corner of screen rect and color       */\r
9755 \r
9756 static void\r
9757 ScreenSquare(column, row, pt)\r
9758      int column; int row; POINT * pt;\r
9759 {\r
9760   if (flipView) {\r
9761     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9762     pt->y = lineGap + row * (squareSize + lineGap);\r
9763   } else {\r
9764     pt->x = lineGap + column * (squareSize + lineGap);\r
9765     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9766   }\r
9767 }\r
9768 \r
9769 /*      Generate a series of frame coords from start->mid->finish.\r
9770         The movement rate doubles until the half way point is\r
9771         reached, then halves back down to the final destination,\r
9772         which gives a nice slow in/out effect. The algorithmn\r
9773         may seem to generate too many intermediates for short\r
9774         moves, but remember that the purpose is to attract the\r
9775         viewers attention to the piece about to be moved and\r
9776         then to where it ends up. Too few frames would be less\r
9777         noticeable.                                             */\r
9778 \r
9779 static void\r
9780 Tween(start, mid, finish, factor, frames, nFrames)\r
9781      POINT * start; POINT * mid;\r
9782      POINT * finish; int factor;\r
9783      POINT frames[]; int * nFrames;\r
9784 {\r
9785   int n, fraction = 1, count = 0;\r
9786 \r
9787   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9788   for (n = 0; n < factor; n++)\r
9789     fraction *= 2;\r
9790   for (n = 0; n < factor; n++) {\r
9791     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9792     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9793     count ++;\r
9794     fraction = fraction / 2;\r
9795   }\r
9796   \r
9797   /* Midpoint */\r
9798   frames[count] = *mid;\r
9799   count ++;\r
9800   \r
9801   /* Slow out, stepping 1/2, then 1/4, ... */\r
9802   fraction = 2;\r
9803   for (n = 0; n < factor; n++) {\r
9804     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9805     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9806     count ++;\r
9807     fraction = fraction * 2;\r
9808   }\r
9809   *nFrames = count;\r
9810 }\r
9811 \r
9812 void\r
9813 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9814 {\r
9815     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9816 \r
9817     EvalGraphSet( first, last, current, pvInfoList );\r
9818 }\r
9819 \r
9820 void\r
9821 SettingsPopUp(ChessProgramState *cps)\r
9822 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9823       EngineOptionsPopup(savedHwnd, cps);\r
9824 }\r
9825 \r
9826 int flock(int fid, int code)\r
9827 {\r
9828     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9829     OVERLAPPED ov;\r
9830     ov.hEvent = NULL;\r
9831     ov.Offset = 0;\r
9832     ov.OffsetHigh = 0;\r
9833     switch(code) {\r
9834       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9835       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9836       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9837       default: return -1;\r
9838     }\r
9839     return 0;\r
9840 }\r