b86a399157c479292af2011e14d8af2d2cc8cb90
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern int ics_type;\r
104 \r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
106 VOID NewVariantPopup(HWND hwnd);\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
108                    /*char*/int promoChar));\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127   ChessSquare piece;\r
128 } DragInfo;\r
129 \r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
131 \r
132 typedef struct {\r
133   POINT sq[2];    /* board coordinates of from, to squares */\r
134 } HighlightInfo;\r
135 \r
136 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
140 \r
141 typedef struct { // [HGM] atomic\r
142   int fromX, fromY, toX, toY, radius;\r
143 } ExplodeInfo;\r
144 \r
145 static ExplodeInfo explodeInfo;\r
146 \r
147 /* Window class names */\r
148 char szAppName[] = "WinBoard";\r
149 char szConsoleName[] = "WBConsole";\r
150 \r
151 /* Title bar text */\r
152 char szTitle[] = "WinBoard";\r
153 char szConsoleTitle[] = "I C S Interaction";\r
154 \r
155 char *programName;\r
156 char *settingsFileName;\r
157 Boolean saveSettingsOnExit;\r
158 char installDir[MSG_SIZ];\r
159 int errorExitStatus;\r
160 \r
161 BoardSize boardSize;\r
162 Boolean chessProgram;\r
163 //static int boardX, boardY;\r
164 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
165 int squareSize, lineGap, minorSize;\r
166 static int winW, winH;\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
168 static int logoHeight = 0;\r
169 static char messageText[MESSAGE_TEXT_MAX];\r
170 static int clockTimerEvent = 0;\r
171 static int loadGameTimerEvent = 0;\r
172 static int analysisTimerEvent = 0;\r
173 static DelayedEventCallback delayedTimerCallback;\r
174 static int delayedTimerEvent = 0;\r
175 static int buttonCount = 2;\r
176 char *icsTextMenuString;\r
177 char *icsNames;\r
178 char *firstChessProgramNames;\r
179 char *secondChessProgramNames;\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 Boolean alwaysOnTop = FALSE;\r
185 RECT boardRect;\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
187   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
188 HPALETTE hPal;\r
189 ColorClass currentColorClass;\r
190 \r
191 static HWND savedHwnd;\r
192 HWND hCommPort = NULL;    /* currently open comm port */\r
193 static HWND hwndPause;    /* pause button */\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,\r
196   blackSquareBrush, /* [HGM] for band between board and holdings */\r
197   explodeBrush,     /* [HGM] atomic */\r
198   markerBrush,      /* [HGM] markers */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 static HBITMAP liteBackTexture = NULL;\r
214 static HBITMAP darkBackTexture = NULL;\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int backTextureSquareSize = 0;\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
219 \r
220 #if __GNUC__ && !defined(_winmajor)\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
222 #else\r
223 #if defined(_winmajor)\r
224 #define oldDialog (_winmajor < 4)\r
225 #else\r
226 #define oldDialog 0\r
227 #endif\r
228 #endif\r
229 \r
230 #define INTERNATIONAL\r
231 \r
232 #ifdef INTERNATIONAL\r
233 #  define _(s) T_(s)\r
234 #  define N_(s) s\r
235 #else\r
236 #  define _(s) s\r
237 #  define N_(s) s\r
238 #  define T_(s) s\r
239 #  define Translate(x, y)\r
240 #  define LoadLanguageFile(s)\r
241 #endif\r
242 \r
243 #ifdef INTERNATIONAL\r
244 \r
245 Boolean barbaric; // flag indicating if translation is needed\r
246 \r
247 // list of item numbers used in each dialog (used to alter language at run time)\r
248 \r
249 #define ABOUTBOX -1  /* not sure why these are needed */\r
250 #define ABOUTBOX2 -1\r
251 \r
252 int dialogItems[][40] = {\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
255   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
257 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
258   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
259 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
260 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
261   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
262 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
263 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
264   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
265 { ABOUTBOX2, IDC_ChessBoard }, \r
266 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
267   OPT_GameListClose, IDC_GameListDoFilter }, \r
268 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
269 { DLG_Error, IDOK }, \r
270 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
271   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
272 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
273 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
274   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
275   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
276 { DLG_IndexNumber, IDC_Index }, \r
277 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
278 { DLG_TypeInName, IDOK, IDCANCEL }, \r
279 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
280   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
281 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
282   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
283   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
284   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
285   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
286   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
287   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
288 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
289   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
290   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
291   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
292   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
293   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
294   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
295   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
296   GPB_General, GPB_Alarm }, \r
297 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
298   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
299   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
300   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
301   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
302   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
303   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
304   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
305 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
306   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
307   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
308   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
309   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
310   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
311   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
312   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
313   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
314 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
315   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
316   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
317   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
318 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
319 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
320   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
321 { DLG_MoveHistory }, \r
322 { DLG_EvalGraph }, \r
323 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
324 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
325 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
326   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
327   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
328   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
329 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
330   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
331   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
332 { 0 }\r
333 };\r
334 \r
335 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
336 static int lastChecked;\r
337 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
338 extern int tinyLayout;\r
339 extern char * menuBarText[][10];\r
340 \r
341 void\r
342 LoadLanguageFile(char *name)\r
343 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
344     FILE *f;\r
345     int i=0, j=0, n=0, k;\r
346     char buf[MSG_SIZ];\r
347 \r
348     if(!name || name[0] == NULLCHAR) return;\r
349       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
350     appData.language = oldLanguage;\r
351     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
352     if((f = fopen(buf, "r")) == NULL) return;\r
353     while((k = fgetc(f)) != EOF) {\r
354         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
355         languageBuf[i] = k;\r
356         if(k == '\n') {\r
357             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
358                 char *p;\r
359                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
360                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
361                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
362                         english[j] = languageBuf + n + 1; *p = 0;\r
363                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
364 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
365                     }\r
366                 }\r
367             }\r
368             n = i + 1;\r
369         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
370             switch(k) {\r
371               case 'n': k = '\n'; break;\r
372               case 'r': k = '\r'; break;\r
373               case 't': k = '\t'; break;\r
374             }\r
375             languageBuf[--i] = k;\r
376         }\r
377         i++;\r
378     }\r
379     fclose(f);\r
380     barbaric = (j != 0);\r
381     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
382 }\r
383 \r
384 char *\r
385 T_(char *s)\r
386 {   // return the translation of the given string\r
387     // efficiency can be improved a lot...\r
388     int i=0;\r
389 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
390     if(!barbaric) return s;\r
391     if(!s) return ""; // sanity\r
392     while(english[i]) {\r
393         if(!strcmp(s, english[i])) return foreign[i];\r
394         i++;\r
395     }\r
396     return s;\r
397 }\r
398 \r
399 void\r
400 Translate(HWND hDlg, int dialogID)\r
401 {   // translate all text items in the given dialog\r
402     int i=0, j, k;\r
403     char buf[MSG_SIZ], *s;\r
404     if(!barbaric) return;\r
405     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
406     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
407     GetWindowText( hDlg, buf, MSG_SIZ );\r
408     s = T_(buf);\r
409     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
410     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
411         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
412         if(strlen(buf) == 0) continue;\r
413         s = T_(buf);\r
414         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
415     }\r
416 }\r
417 \r
418 HMENU\r
419 TranslateOneMenu(int i, HMENU subMenu)\r
420 {\r
421     int j;\r
422     static MENUITEMINFO info;\r
423 \r
424     info.cbSize = sizeof(MENUITEMINFO);\r
425     info.fMask = MIIM_STATE | MIIM_TYPE;\r
426           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
427             char buf[MSG_SIZ];\r
428             info.dwTypeData = buf;\r
429             info.cch = sizeof(buf);\r
430             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
431             if(i < 10) {
432                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
433                 else menuText[i][j] = strdup(buf); // remember original on first change\r
434             }\r
435             if(buf[0] == NULLCHAR) continue;\r
436             info.dwTypeData = T_(buf);\r
437             info.cch = strlen(buf)+1;\r
438             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
439           }\r
440     return subMenu;\r
441 }\r
442 \r
443 void\r
444 TranslateMenus(int addLanguage)\r
445 {\r
446     int i;\r
447     WIN32_FIND_DATA fileData;\r
448     HANDLE hFind;\r
449 #define IDM_English 1970\r
450     if(1) {\r
451         HMENU mainMenu = GetMenu(hwndMain);\r
452         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
453           HMENU subMenu = GetSubMenu(mainMenu, i);\r
454           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
455                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
456           TranslateOneMenu(i, subMenu);\r
457         }\r
458         DrawMenuBar(hwndMain);\r
459     }\r
460 \r
461     if(!addLanguage) return;\r
462     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
463         HMENU mainMenu = GetMenu(hwndMain);\r
464         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
465         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
466         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
467         i = 0; lastChecked = IDM_English;\r
468         do {\r
469             char *p, *q = fileData.cFileName;\r
470             int checkFlag = MF_UNCHECKED;\r
471             languageFile[i] = strdup(q);\r
472             if(barbaric && !strcmp(oldLanguage, q)) {\r
473                 checkFlag = MF_CHECKED;\r
474                 lastChecked = IDM_English + i + 1;\r
475                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
476             }\r
477             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
478             p = strstr(fileData.cFileName, ".lng");\r
479             if(p) *p = 0;\r
480             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
481         } while(FindNextFile(hFind, &fileData));\r
482         FindClose(hFind);\r
483     }\r
484 }\r
485 \r
486 #endif\r
487 \r
488 typedef struct {\r
489   char *name;\r
490   int squareSize;\r
491   int lineGap;\r
492   int smallLayout;\r
493   int tinyLayout;\r
494   int cliWidth, cliHeight;\r
495 } SizeInfo;\r
496 \r
497 SizeInfo sizeInfo[] = \r
498 {\r
499   { "tiny",     21, 0, 1, 1, 0, 0 },\r
500   { "teeny",    25, 1, 1, 1, 0, 0 },\r
501   { "dinky",    29, 1, 1, 1, 0, 0 },\r
502   { "petite",   33, 1, 1, 1, 0, 0 },\r
503   { "slim",     37, 2, 1, 0, 0, 0 },\r
504   { "small",    40, 2, 1, 0, 0, 0 },\r
505   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
506   { "middling", 49, 2, 0, 0, 0, 0 },\r
507   { "average",  54, 2, 0, 0, 0, 0 },\r
508   { "moderate", 58, 3, 0, 0, 0, 0 },\r
509   { "medium",   64, 3, 0, 0, 0, 0 },\r
510   { "bulky",    72, 3, 0, 0, 0, 0 },\r
511   { "large",    80, 3, 0, 0, 0, 0 },\r
512   { "big",      87, 3, 0, 0, 0, 0 },\r
513   { "huge",     95, 3, 0, 0, 0, 0 },\r
514   { "giant",    108, 3, 0, 0, 0, 0 },\r
515   { "colossal", 116, 4, 0, 0, 0, 0 },\r
516   { "titanic",  129, 4, 0, 0, 0, 0 },\r
517   { NULL, 0, 0, 0, 0, 0, 0 }\r
518 };\r
519 \r
520 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
521 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
522 {\r
523   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
524   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
525   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
526   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
527   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
528   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
529   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
530   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
531   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
532   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
533   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
534   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
535   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
536   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
537   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
538   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
539   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
540   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
541 };\r
542 \r
543 MyFont *font[NUM_SIZES][NUM_FONTS];\r
544 \r
545 typedef struct {\r
546   char *label;\r
547   int id;\r
548   HWND hwnd;\r
549   WNDPROC wndproc;\r
550 } MyButtonDesc;\r
551 \r
552 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
553 #define N_BUTTONS 5\r
554 \r
555 MyButtonDesc buttonDesc[N_BUTTONS] =\r
556 {\r
557   {"<<", IDM_ToStart, NULL, NULL},\r
558   {"<", IDM_Backward, NULL, NULL},\r
559   {"P", IDM_Pause, NULL, NULL},\r
560   {">", IDM_Forward, NULL, NULL},\r
561   {">>", IDM_ToEnd, NULL, NULL},\r
562 };\r
563 \r
564 int tinyLayout = 0, smallLayout = 0;\r
565 #define MENU_BAR_ITEMS 9\r
566 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
567   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
568   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
569 };\r
570 \r
571 \r
572 MySound sounds[(int)NSoundClasses];\r
573 MyTextAttribs textAttribs[(int)NColorClasses];\r
574 \r
575 MyColorizeAttribs colorizeAttribs[] = {\r
576   { (COLORREF)0, 0, N_("Shout Text") },\r
577   { (COLORREF)0, 0, N_("SShout/CShout") },\r
578   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
579   { (COLORREF)0, 0, N_("Channel Text") },\r
580   { (COLORREF)0, 0, N_("Kibitz Text") },\r
581   { (COLORREF)0, 0, N_("Tell Text") },\r
582   { (COLORREF)0, 0, N_("Challenge Text") },\r
583   { (COLORREF)0, 0, N_("Request Text") },\r
584   { (COLORREF)0, 0, N_("Seek Text") },\r
585   { (COLORREF)0, 0, N_("Normal Text") },\r
586   { (COLORREF)0, 0, N_("None") }\r
587 };\r
588 \r
589 \r
590 \r
591 static char *commentTitle;\r
592 static char *commentText;\r
593 static int commentIndex;\r
594 static Boolean editComment = FALSE;\r
595 \r
596 \r
597 char errorTitle[MSG_SIZ];\r
598 char errorMessage[2*MSG_SIZ];\r
599 HWND errorDialog = NULL;\r
600 BOOLEAN moveErrorMessageUp = FALSE;\r
601 BOOLEAN consoleEcho = TRUE;\r
602 CHARFORMAT consoleCF;\r
603 COLORREF consoleBackgroundColor;\r
604 \r
605 char *programVersion;\r
606 \r
607 #define CPReal 1\r
608 #define CPComm 2\r
609 #define CPSock 3\r
610 #define CPRcmd 4\r
611 typedef int CPKind;\r
612 \r
613 typedef struct {\r
614   CPKind kind;\r
615   HANDLE hProcess;\r
616   DWORD pid;\r
617   HANDLE hTo;\r
618   HANDLE hFrom;\r
619   SOCKET sock;\r
620   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
621 } ChildProc;\r
622 \r
623 #define INPUT_SOURCE_BUF_SIZE 4096\r
624 \r
625 typedef struct _InputSource {\r
626   CPKind kind;\r
627   HANDLE hFile;\r
628   SOCKET sock;\r
629   int lineByLine;\r
630   HANDLE hThread;\r
631   DWORD id;\r
632   char buf[INPUT_SOURCE_BUF_SIZE];\r
633   char *next;\r
634   DWORD count;\r
635   int error;\r
636   InputCallback func;\r
637   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
638   VOIDSTAR closure;\r
639 } InputSource;\r
640 \r
641 InputSource *consoleInputSource;\r
642 \r
643 DCB dcb;\r
644 \r
645 /* forward */\r
646 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
647 VOID ConsoleCreate();\r
648 LRESULT CALLBACK\r
649   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
650 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
651 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
652 VOID ParseCommSettings(char *arg, DCB *dcb);\r
653 LRESULT CALLBACK\r
654   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
655 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
656 void ParseIcsTextMenu(char *icsTextMenuString);\r
657 VOID PopUpNameDialog(char firstchar);\r
658 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
659 \r
660 /* [AS] */\r
661 int NewGameFRC();\r
662 int GameListOptions();\r
663 \r
664 int dummy; // [HGM] for obsolete args\r
665 \r
666 HWND hwndMain = NULL;        /* root window*/\r
667 HWND hwndConsole = NULL;\r
668 HWND commentDialog = NULL;\r
669 HWND moveHistoryDialog = NULL;\r
670 HWND evalGraphDialog = NULL;\r
671 HWND engineOutputDialog = NULL;\r
672 HWND gameListDialog = NULL;\r
673 HWND editTagsDialog = NULL;\r
674 \r
675 int commentUp = FALSE;\r
676 \r
677 WindowPlacement wpMain;\r
678 WindowPlacement wpConsole;\r
679 WindowPlacement wpComment;\r
680 WindowPlacement wpMoveHistory;\r
681 WindowPlacement wpEvalGraph;\r
682 WindowPlacement wpEngineOutput;\r
683 WindowPlacement wpGameList;\r
684 WindowPlacement wpTags;\r
685 \r
686 VOID EngineOptionsPopup(); // [HGM] settings\r
687 \r
688 VOID GothicPopUp(char *title, VariantClass variant);\r
689 /*\r
690  * Setting "frozen" should disable all user input other than deleting\r
691  * the window.  We do this while engines are initializing themselves.\r
692  */\r
693 static int frozen = 0;\r
694 static int oldMenuItemState[MENU_BAR_ITEMS];\r
695 void FreezeUI()\r
696 {\r
697   HMENU hmenu;\r
698   int i;\r
699 \r
700   if (frozen) return;\r
701   frozen = 1;\r
702   hmenu = GetMenu(hwndMain);\r
703   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
704     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
705   }\r
706   DrawMenuBar(hwndMain);\r
707 }\r
708 \r
709 /* Undo a FreezeUI */\r
710 void ThawUI()\r
711 {\r
712   HMENU hmenu;\r
713   int i;\r
714 \r
715   if (!frozen) return;\r
716   frozen = 0;\r
717   hmenu = GetMenu(hwndMain);\r
718   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
719     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
720   }\r
721   DrawMenuBar(hwndMain);\r
722 }\r
723 \r
724 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
725 \r
726 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
727 #ifdef JAWS\r
728 #include "jaws.c"\r
729 #else\r
730 #define JAWS_INIT\r
731 #define JAWS_ARGS\r
732 #define JAWS_ALT_INTERCEPT\r
733 #define JAWS_KB_NAVIGATION\r
734 #define JAWS_MENU_ITEMS\r
735 #define JAWS_SILENCE\r
736 #define JAWS_REPLAY\r
737 #define JAWS_ACCEL\r
738 #define JAWS_COPYRIGHT\r
739 #define JAWS_DELETE(X) X\r
740 #define SAYMACHINEMOVE()\r
741 #define SAY(X)\r
742 #endif\r
743 \r
744 /*---------------------------------------------------------------------------*\\r
745  *\r
746  * WinMain\r
747  *\r
748 \*---------------------------------------------------------------------------*/\r
749 \r
750 int APIENTRY\r
751 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
752         LPSTR lpCmdLine, int nCmdShow)\r
753 {\r
754   MSG msg;\r
755   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
756 //  INITCOMMONCONTROLSEX ex;\r
757 \r
758   debugFP = stderr;\r
759 \r
760   LoadLibrary("RICHED32.DLL");\r
761   consoleCF.cbSize = sizeof(CHARFORMAT);\r
762 \r
763   if (!InitApplication(hInstance)) {\r
764     return (FALSE);\r
765   }\r
766   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
767     return (FALSE);\r
768   }\r
769 \r
770   JAWS_INIT\r
771   TranslateMenus(1);\r
772 \r
773 //  InitCommonControlsEx(&ex);\r
774   InitCommonControls();\r
775 \r
776   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
777   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
778   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
779 \r
780   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
781 \r
782   while (GetMessage(&msg, /* message structure */\r
783                     NULL, /* handle of window receiving the message */\r
784                     0,    /* lowest message to examine */\r
785                     0))   /* highest message to examine */\r
786     {\r
787 \r
788       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
789         // [HGM] navigate: switch between all windows with tab\r
790         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
791         int i, currentElement = 0;\r
792 \r
793         // first determine what element of the chain we come from (if any)\r
794         if(appData.icsActive) {\r
795             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
796             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
797         }\r
798         if(engineOutputDialog && EngineOutputIsUp()) {\r
799             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
800             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
801         }\r
802         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
803             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
804         }\r
805         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
806         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
807         if(msg.hwnd == e1)                 currentElement = 2; else\r
808         if(msg.hwnd == e2)                 currentElement = 3; else\r
809         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
810         if(msg.hwnd == mh)                currentElement = 4; else\r
811         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
812         if(msg.hwnd == hText)  currentElement = 5; else\r
813         if(msg.hwnd == hInput) currentElement = 6; else\r
814         for (i = 0; i < N_BUTTONS; i++) {\r
815             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
816         }\r
817 \r
818         // determine where to go to\r
819         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
820           do {\r
821             currentElement = (currentElement + direction) % 7;\r
822             switch(currentElement) {\r
823                 case 0:\r
824                   h = hwndMain; break; // passing this case always makes the loop exit\r
825                 case 1:\r
826                   h = buttonDesc[0].hwnd; break; // could be NULL\r
827                 case 2:\r
828                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
829                   h = e1; break;\r
830                 case 3:\r
831                   if(!EngineOutputIsUp()) continue;\r
832                   h = e2; break;\r
833                 case 4:\r
834                   if(!MoveHistoryIsUp()) continue;\r
835                   h = mh; break;\r
836 //              case 6: // input to eval graph does not seem to get here!\r
837 //                if(!EvalGraphIsUp()) continue;\r
838 //                h = evalGraphDialog; break;\r
839                 case 5:\r
840                   if(!appData.icsActive) continue;\r
841                   SAY("display");\r
842                   h = hText; break;\r
843                 case 6:\r
844                   if(!appData.icsActive) continue;\r
845                   SAY("input");\r
846                   h = hInput; break;\r
847             }\r
848           } while(h == 0);\r
849 \r
850           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
851           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
852           SetFocus(h);\r
853 \r
854           continue; // this message now has been processed\r
855         }\r
856       }\r
857 \r
858       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
859           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
860           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
861           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
862           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
863           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
864           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
865           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
866           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
867           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
868         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
869         for(i=0; i<MAX_CHAT; i++) \r
870             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
871                 done = 1; break;\r
872         }\r
873         if(done) continue; // [HGM] chat: end patch\r
874         TranslateMessage(&msg); /* Translates virtual key codes */\r
875         DispatchMessage(&msg);  /* Dispatches message to window */\r
876       }\r
877     }\r
878 \r
879 \r
880   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
881 }\r
882 \r
883 /*---------------------------------------------------------------------------*\\r
884  *\r
885  * Initialization functions\r
886  *\r
887 \*---------------------------------------------------------------------------*/\r
888 \r
889 void\r
890 SetUserLogo()\r
891 {   // update user logo if necessary\r
892     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
893 \r
894     if(appData.autoLogo) {\r
895           curName = UserName();\r
896           if(strcmp(curName, oldUserName)) {\r
897                 GetCurrentDirectory(MSG_SIZ, dir);\r
898                 SetCurrentDirectory(installDir);\r
899                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
900                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
901                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
902                 if(userLogo == NULL)\r
903                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
904                 SetCurrentDirectory(dir); /* return to prev directory */\r
905           }\r
906     }\r
907 }\r
908 \r
909 BOOL\r
910 InitApplication(HINSTANCE hInstance)\r
911 {\r
912   WNDCLASS wc;\r
913 \r
914   /* Fill in window class structure with parameters that describe the */\r
915   /* main window. */\r
916 \r
917   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
918   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
919   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
920   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
921   wc.hInstance     = hInstance;         /* Owner of this class */\r
922   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
923   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
924   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
925   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
926   wc.lpszClassName = szAppName;                 /* Name to register as */\r
927 \r
928   /* Register the window class and return success/failure code. */\r
929   if (!RegisterClass(&wc)) return FALSE;\r
930 \r
931   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
932   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
933   wc.cbClsExtra    = 0;\r
934   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
935   wc.hInstance     = hInstance;\r
936   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
937   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
938   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
939   wc.lpszMenuName  = NULL;\r
940   wc.lpszClassName = szConsoleName;\r
941 \r
942   if (!RegisterClass(&wc)) return FALSE;\r
943   return TRUE;\r
944 }\r
945 \r
946 \r
947 /* Set by InitInstance, used by EnsureOnScreen */\r
948 int screenHeight, screenWidth;\r
949 \r
950 void\r
951 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
952 {\r
953 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
954   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
955   if (*x > screenWidth - 32) *x = 0;\r
956   if (*y > screenHeight - 32) *y = 0;\r
957   if (*x < minX) *x = minX;\r
958   if (*y < minY) *y = minY;\r
959 }\r
960 \r
961 VOID\r
962 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
963 {\r
964   char buf[MSG_SIZ], dir[MSG_SIZ];\r
965   GetCurrentDirectory(MSG_SIZ, dir);\r
966   SetCurrentDirectory(installDir);\r
967   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
968       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
969 \r
970       if (cps->programLogo == NULL && appData.debugMode) {\r
971           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
972       }\r
973   } else if(appData.autoLogo) {\r
974       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
975         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
976         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
977       } else\r
978       if(appData.directory[n] && appData.directory[n][0]) {\r
979         SetCurrentDirectory(appData.directory[n]);\r
980         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
981       }\r
982   }\r
983   SetCurrentDirectory(dir); /* return to prev directory */\r
984 }\r
985 \r
986 BOOL\r
987 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
988 {\r
989   HWND hwnd; /* Main window handle. */\r
990   int ibs;\r
991   WINDOWPLACEMENT wp;\r
992   char *filepart;\r
993 \r
994   hInst = hInstance;    /* Store instance handle in our global variable */\r
995   programName = szAppName;\r
996 \r
997   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
998     *filepart = NULLCHAR;\r
999   } else {\r
1000     GetCurrentDirectory(MSG_SIZ, installDir);\r
1001   }\r
1002   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1003   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1004   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1005   /* xboard, and older WinBoards, controlled the move sound with the\r
1006      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1007      always turn the option on (so that the backend will call us),\r
1008      then let the user turn the sound off by setting it to silence if\r
1009      desired.  To accommodate old winboard.ini files saved by old\r
1010      versions of WinBoard, we also turn off the sound if the option\r
1011      was initially set to false. [HGM] taken out of InitAppData */\r
1012   if (!appData.ringBellAfterMoves) {\r
1013     sounds[(int)SoundMove].name = strdup("");\r
1014     appData.ringBellAfterMoves = TRUE;\r
1015   }\r
1016   if (appData.debugMode) {\r
1017     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1018     setbuf(debugFP, NULL);\r
1019   }\r
1020 \r
1021   LoadLanguageFile(appData.language);\r
1022 \r
1023   InitBackEnd1();\r
1024 \r
1025 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1026 //  InitEngineUCI( installDir, &second );\r
1027 \r
1028   /* Create a main window for this application instance. */\r
1029   hwnd = CreateWindow(szAppName, szTitle,\r
1030                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1031                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1032                       NULL, NULL, hInstance, NULL);\r
1033   hwndMain = hwnd;\r
1034 \r
1035   /* If window could not be created, return "failure" */\r
1036   if (!hwnd) {\r
1037     return (FALSE);\r
1038   }\r
1039 \r
1040   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1041   LoadLogo(&first, 0, FALSE);\r
1042   LoadLogo(&second, 1, appData.icsActive);\r
1043 \r
1044   SetUserLogo();\r
1045 \r
1046   iconWhite = LoadIcon(hInstance, "icon_white");\r
1047   iconBlack = LoadIcon(hInstance, "icon_black");\r
1048   iconCurrent = iconWhite;\r
1049   InitDrawingColors();\r
1050   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1051   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1052   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1053     /* Compute window size for each board size, and use the largest\r
1054        size that fits on this screen as the default. */\r
1055     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1056     if (boardSize == (BoardSize)-1 &&\r
1057         winH <= screenHeight\r
1058            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1059         && winW <= screenWidth) {\r
1060       boardSize = (BoardSize)ibs;\r
1061     }\r
1062   }\r
1063 \r
1064   InitDrawingSizes(boardSize, 0);\r
1065   InitMenuChecks();\r
1066   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1067 \r
1068   /* [AS] Load textures if specified */\r
1069   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1070   \r
1071   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1072       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1073       liteBackTextureMode = appData.liteBackTextureMode;\r
1074 \r
1075       if (liteBackTexture == NULL && appData.debugMode) {\r
1076           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1077       }\r
1078   }\r
1079   \r
1080   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1081       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1082       darkBackTextureMode = appData.darkBackTextureMode;\r
1083 \r
1084       if (darkBackTexture == NULL && appData.debugMode) {\r
1085           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1086       }\r
1087   }\r
1088 \r
1089   mysrandom( (unsigned) time(NULL) );\r
1090 \r
1091   /* [AS] Restore layout */\r
1092   if( wpMoveHistory.visible ) {\r
1093       MoveHistoryPopUp();\r
1094   }\r
1095 \r
1096   if( wpEvalGraph.visible ) {\r
1097       EvalGraphPopUp();\r
1098   }\r
1099 \r
1100   if( wpEngineOutput.visible ) {\r
1101       EngineOutputPopUp();\r
1102   }\r
1103 \r
1104   /* Make the window visible; update its client area; and return "success" */\r
1105   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1106   wp.length = sizeof(WINDOWPLACEMENT);\r
1107   wp.flags = 0;\r
1108   wp.showCmd = nCmdShow;\r
1109   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1110   wp.rcNormalPosition.left = wpMain.x;\r
1111   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1112   wp.rcNormalPosition.top = wpMain.y;\r
1113   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1114   SetWindowPlacement(hwndMain, &wp);\r
1115 \r
1116   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1117 \r
1118   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1119                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1120 \r
1121   if (hwndConsole) {\r
1122 #if AOT_CONSOLE\r
1123     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1124                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1125 #endif\r
1126     ShowWindow(hwndConsole, nCmdShow);\r
1127     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1128       char buf[MSG_SIZ], *p = buf, *q;\r
1129         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1130       do {\r
1131         q = strchr(p, ';');\r
1132         if(q) *q++ = 0;\r
1133         if(*p) ChatPopUp(p);\r
1134       } while(p=q);\r
1135     }\r
1136     SetActiveWindow(hwndConsole);\r
1137   }\r
1138   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1139   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1140 \r
1141   return TRUE;\r
1142 \r
1143 }\r
1144 \r
1145 VOID\r
1146 InitMenuChecks()\r
1147 {\r
1148   HMENU hmenu = GetMenu(hwndMain);\r
1149 \r
1150   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1151                         MF_BYCOMMAND|((appData.icsActive &&\r
1152                                        *appData.icsCommPort != NULLCHAR) ?\r
1153                                       MF_ENABLED : MF_GRAYED));\r
1154   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1155                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1156                                      MF_CHECKED : MF_UNCHECKED));\r
1157 }\r
1158 \r
1159 //---------------------------------------------------------------------------------------------------------\r
1160 \r
1161 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1162 #define XBOARD FALSE\r
1163 \r
1164 #define OPTCHAR "/"\r
1165 #define SEPCHAR "="\r
1166 \r
1167 #include "args.h"\r
1168 \r
1169 // front-end part of option handling\r
1170 \r
1171 VOID\r
1172 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1173 {\r
1174   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1175   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1176   DeleteDC(hdc);\r
1177   lf->lfWidth = 0;\r
1178   lf->lfEscapement = 0;\r
1179   lf->lfOrientation = 0;\r
1180   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1181   lf->lfItalic = mfp->italic;\r
1182   lf->lfUnderline = mfp->underline;\r
1183   lf->lfStrikeOut = mfp->strikeout;\r
1184   lf->lfCharSet = mfp->charset;\r
1185   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1186   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1187   lf->lfQuality = DEFAULT_QUALITY;\r
1188   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1189     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1190 }\r
1191 \r
1192 void\r
1193 CreateFontInMF(MyFont *mf)\r
1194\r
1195   LFfromMFP(&mf->lf, &mf->mfp);\r
1196   if (mf->hf) DeleteObject(mf->hf);\r
1197   mf->hf = CreateFontIndirect(&mf->lf);\r
1198 }\r
1199 \r
1200 // [HGM] This platform-dependent table provides the location for storing the color info\r
1201 void *\r
1202 colorVariable[] = {\r
1203   &whitePieceColor, \r
1204   &blackPieceColor, \r
1205   &lightSquareColor,\r
1206   &darkSquareColor, \r
1207   &highlightSquareColor,\r
1208   &premoveHighlightColor,\r
1209   NULL,\r
1210   &consoleBackgroundColor,\r
1211   &appData.fontForeColorWhite,\r
1212   &appData.fontBackColorWhite,\r
1213   &appData.fontForeColorBlack,\r
1214   &appData.fontBackColorBlack,\r
1215   &appData.evalHistColorWhite,\r
1216   &appData.evalHistColorBlack,\r
1217   &appData.highlightArrowColor,\r
1218 };\r
1219 \r
1220 /* Command line font name parser.  NULL name means do nothing.\r
1221    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1222    For backward compatibility, syntax without the colon is also\r
1223    accepted, but font names with digits in them won't work in that case.\r
1224 */\r
1225 VOID\r
1226 ParseFontName(char *name, MyFontParams *mfp)\r
1227 {\r
1228   char *p, *q;\r
1229   if (name == NULL) return;\r
1230   p = name;\r
1231   q = strchr(p, ':');\r
1232   if (q) {\r
1233     if (q - p >= sizeof(mfp->faceName))\r
1234       ExitArgError(_("Font name too long:"), name);\r
1235     memcpy(mfp->faceName, p, q - p);\r
1236     mfp->faceName[q - p] = NULLCHAR;\r
1237     p = q + 1;\r
1238   } else {\r
1239     q = mfp->faceName;\r
1240     while (*p && !isdigit(*p)) {\r
1241       *q++ = *p++;\r
1242       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1243         ExitArgError(_("Font name too long:"), name);\r
1244     }\r
1245     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1246     *q = NULLCHAR;\r
1247   }\r
1248   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1249   mfp->pointSize = (float) atof(p);\r
1250   mfp->bold = (strchr(p, 'b') != NULL);\r
1251   mfp->italic = (strchr(p, 'i') != NULL);\r
1252   mfp->underline = (strchr(p, 'u') != NULL);\r
1253   mfp->strikeout = (strchr(p, 's') != NULL);\r
1254   mfp->charset = DEFAULT_CHARSET;\r
1255   q = strchr(p, 'c');\r
1256   if (q)\r
1257     mfp->charset = (BYTE) atoi(q+1);\r
1258 }\r
1259 \r
1260 void\r
1261 ParseFont(char *name, int number)\r
1262 { // wrapper to shield back-end from 'font'\r
1263   ParseFontName(name, &font[boardSize][number]->mfp);\r
1264 }\r
1265 \r
1266 void\r
1267 SetFontDefaults()\r
1268 { // in WB  we have a 2D array of fonts; this initializes their description\r
1269   int i, j;\r
1270   /* Point font array elements to structures and\r
1271      parse default font names */\r
1272   for (i=0; i<NUM_FONTS; i++) {\r
1273     for (j=0; j<NUM_SIZES; j++) {\r
1274       font[j][i] = &fontRec[j][i];\r
1275       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1276     }\r
1277   }\r
1278 }\r
1279 \r
1280 void\r
1281 CreateFonts()\r
1282 { // here we create the actual fonts from the selected descriptions\r
1283   int i, j;\r
1284   for (i=0; i<NUM_FONTS; i++) {\r
1285     for (j=0; j<NUM_SIZES; j++) {\r
1286       CreateFontInMF(font[j][i]);\r
1287     }\r
1288   }\r
1289 }\r
1290 /* Color name parser.\r
1291    X version accepts X color names, but this one\r
1292    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1293 COLORREF\r
1294 ParseColorName(char *name)\r
1295 {\r
1296   int red, green, blue, count;\r
1297   char buf[MSG_SIZ];\r
1298 \r
1299   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1300   if (count != 3) {\r
1301     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1302       &red, &green, &blue);\r
1303   }\r
1304   if (count != 3) {\r
1305     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1306     DisplayError(buf, 0);\r
1307     return RGB(0, 0, 0);\r
1308   }\r
1309   return PALETTERGB(red, green, blue);\r
1310 }\r
1311 \r
1312 void\r
1313 ParseColor(int n, char *name)\r
1314 { // for WinBoard the color is an int, which needs to be derived from the string\r
1315   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1316 }\r
1317 \r
1318 void\r
1319 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1320 {\r
1321   char *e = argValue;\r
1322   int eff = 0;\r
1323 \r
1324   while (*e) {\r
1325     if (*e == 'b')      eff |= CFE_BOLD;\r
1326     else if (*e == 'i') eff |= CFE_ITALIC;\r
1327     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1328     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1329     else if (*e == '#' || isdigit(*e)) break;\r
1330     e++;\r
1331   }\r
1332   *effects = eff;\r
1333   *color   = ParseColorName(e);\r
1334 }\r
1335 \r
1336 void\r
1337 ParseTextAttribs(ColorClass cc, char *s)\r
1338 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1339     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1340     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1341 }\r
1342 \r
1343 void\r
1344 ParseBoardSize(void *addr, char *name)\r
1345 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1346   BoardSize bs = SizeTiny;\r
1347   while (sizeInfo[bs].name != NULL) {\r
1348     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1349         *(BoardSize *)addr = bs;\r
1350         return;\r
1351     }\r
1352     bs++;\r
1353   }\r
1354   ExitArgError(_("Unrecognized board size value"), name);\r
1355 }\r
1356 \r
1357 void\r
1358 LoadAllSounds()\r
1359 { // [HGM] import name from appData first\r
1360   ColorClass cc;\r
1361   SoundClass sc;\r
1362   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1363     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1364     textAttribs[cc].sound.data = NULL;\r
1365     MyLoadSound(&textAttribs[cc].sound);\r
1366   }\r
1367   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1368     textAttribs[cc].sound.name = strdup("");\r
1369     textAttribs[cc].sound.data = NULL;\r
1370   }\r
1371   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1372     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1373     sounds[sc].data = NULL;\r
1374     MyLoadSound(&sounds[sc]);\r
1375   }\r
1376 }\r
1377 \r
1378 void\r
1379 SetCommPortDefaults()\r
1380 {\r
1381    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1382   dcb.DCBlength = sizeof(DCB);\r
1383   dcb.BaudRate = 9600;\r
1384   dcb.fBinary = TRUE;\r
1385   dcb.fParity = FALSE;\r
1386   dcb.fOutxCtsFlow = FALSE;\r
1387   dcb.fOutxDsrFlow = FALSE;\r
1388   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1389   dcb.fDsrSensitivity = FALSE;\r
1390   dcb.fTXContinueOnXoff = TRUE;\r
1391   dcb.fOutX = FALSE;\r
1392   dcb.fInX = FALSE;\r
1393   dcb.fNull = FALSE;\r
1394   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1395   dcb.fAbortOnError = FALSE;\r
1396   dcb.ByteSize = 7;\r
1397   dcb.Parity = SPACEPARITY;\r
1398   dcb.StopBits = ONESTOPBIT;\r
1399 }\r
1400 \r
1401 // [HGM] args: these three cases taken out to stay in front-end\r
1402 void\r
1403 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1404 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1405         // while the curent board size determines the element. This system should be ported to XBoard.\r
1406         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1407         int bs;\r
1408         for (bs=0; bs<NUM_SIZES; bs++) {\r
1409           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1410           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1411           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1412             ad->argName, mfp->faceName, mfp->pointSize,\r
1413             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1414             mfp->bold ? "b" : "",\r
1415             mfp->italic ? "i" : "",\r
1416             mfp->underline ? "u" : "",\r
1417             mfp->strikeout ? "s" : "",\r
1418             (int)mfp->charset);\r
1419         }\r
1420       }\r
1421 \r
1422 void\r
1423 ExportSounds()\r
1424 { // [HGM] copy the names from the internal WB variables to appData\r
1425   ColorClass cc;\r
1426   SoundClass sc;\r
1427   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1428     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1429   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1430     (&appData.soundMove)[sc] = sounds[sc].name;\r
1431 }\r
1432 \r
1433 void\r
1434 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1435 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1436         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1437         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1438           (ta->effects & CFE_BOLD) ? "b" : "",\r
1439           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1440           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1441           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1442           (ta->effects) ? " " : "",\r
1443           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1444       }\r
1445 \r
1446 void\r
1447 SaveColor(FILE *f, ArgDescriptor *ad)\r
1448 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1449         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1450         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1451           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1452 }\r
1453 \r
1454 void\r
1455 SaveBoardSize(FILE *f, char *name, void *addr)\r
1456 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1457   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1458 }\r
1459 \r
1460 void\r
1461 ParseCommPortSettings(char *s)\r
1462 { // wrapper to keep dcb from back-end\r
1463   ParseCommSettings(s, &dcb);\r
1464 }\r
1465 \r
1466 void\r
1467 GetWindowCoords()\r
1468 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1469   GetActualPlacement(hwndMain, &wpMain);\r
1470   GetActualPlacement(hwndConsole, &wpConsole);\r
1471   GetActualPlacement(commentDialog, &wpComment);\r
1472   GetActualPlacement(editTagsDialog, &wpTags);\r
1473   GetActualPlacement(gameListDialog, &wpGameList);\r
1474   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1475   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1476   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1477 }\r
1478 \r
1479 void\r
1480 PrintCommPortSettings(FILE *f, char *name)\r
1481 { // wrapper to shield back-end from DCB\r
1482       PrintCommSettings(f, name, &dcb);\r
1483 }\r
1484 \r
1485 int\r
1486 MySearchPath(char *installDir, char *name, char *fullname)\r
1487 {\r
1488   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1489   if(name[0]== '%') {\r
1490     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1491     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1492       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1493       *strchr(buf, '%') = 0;\r
1494       strcat(fullname, getenv(buf));\r
1495       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1496     }\r
1497     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1498     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1499     return (int) strlen(fullname);\r
1500   }\r
1501   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1502 }\r
1503 \r
1504 int\r
1505 MyGetFullPathName(char *name, char *fullname)\r
1506 {\r
1507   char *dummy;\r
1508   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1509 }\r
1510 \r
1511 int\r
1512 MainWindowUp()\r
1513 { // [HGM] args: allows testing if main window is realized from back-end\r
1514   return hwndMain != NULL;\r
1515 }\r
1516 \r
1517 void\r
1518 PopUpStartupDialog()\r
1519 {\r
1520     FARPROC lpProc;\r
1521     \r
1522     LoadLanguageFile(appData.language);\r
1523     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1524     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1525     FreeProcInstance(lpProc);\r
1526 }\r
1527 \r
1528 /*---------------------------------------------------------------------------*\\r
1529  *\r
1530  * GDI board drawing routines\r
1531  *\r
1532 \*---------------------------------------------------------------------------*/\r
1533 \r
1534 /* [AS] Draw square using background texture */\r
1535 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1536 {\r
1537     XFORM   x;\r
1538 \r
1539     if( mode == 0 ) {\r
1540         return; /* Should never happen! */\r
1541     }\r
1542 \r
1543     SetGraphicsMode( dst, GM_ADVANCED );\r
1544 \r
1545     switch( mode ) {\r
1546     case 1:\r
1547         /* Identity */\r
1548         break;\r
1549     case 2:\r
1550         /* X reflection */\r
1551         x.eM11 = -1.0;\r
1552         x.eM12 = 0;\r
1553         x.eM21 = 0;\r
1554         x.eM22 = 1.0;\r
1555         x.eDx = (FLOAT) dw + dx - 1;\r
1556         x.eDy = 0;\r
1557         dx = 0;\r
1558         SetWorldTransform( dst, &x );\r
1559         break;\r
1560     case 3:\r
1561         /* Y reflection */\r
1562         x.eM11 = 1.0;\r
1563         x.eM12 = 0;\r
1564         x.eM21 = 0;\r
1565         x.eM22 = -1.0;\r
1566         x.eDx = 0;\r
1567         x.eDy = (FLOAT) dh + dy - 1;\r
1568         dy = 0;\r
1569         SetWorldTransform( dst, &x );\r
1570         break;\r
1571     case 4:\r
1572         /* X/Y flip */\r
1573         x.eM11 = 0;\r
1574         x.eM12 = 1.0;\r
1575         x.eM21 = 1.0;\r
1576         x.eM22 = 0;\r
1577         x.eDx = (FLOAT) dx;\r
1578         x.eDy = (FLOAT) dy;\r
1579         dx = 0;\r
1580         dy = 0;\r
1581         SetWorldTransform( dst, &x );\r
1582         break;\r
1583     }\r
1584 \r
1585     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1586 \r
1587     x.eM11 = 1.0;\r
1588     x.eM12 = 0;\r
1589     x.eM21 = 0;\r
1590     x.eM22 = 1.0;\r
1591     x.eDx = 0;\r
1592     x.eDy = 0;\r
1593     SetWorldTransform( dst, &x );\r
1594 \r
1595     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1596 }\r
1597 \r
1598 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1599 enum {\r
1600     PM_WP = (int) WhitePawn, \r
1601     PM_WN = (int) WhiteKnight, \r
1602     PM_WB = (int) WhiteBishop, \r
1603     PM_WR = (int) WhiteRook, \r
1604     PM_WQ = (int) WhiteQueen, \r
1605     PM_WF = (int) WhiteFerz, \r
1606     PM_WW = (int) WhiteWazir, \r
1607     PM_WE = (int) WhiteAlfil, \r
1608     PM_WM = (int) WhiteMan, \r
1609     PM_WO = (int) WhiteCannon, \r
1610     PM_WU = (int) WhiteUnicorn, \r
1611     PM_WH = (int) WhiteNightrider, \r
1612     PM_WA = (int) WhiteAngel, \r
1613     PM_WC = (int) WhiteMarshall, \r
1614     PM_WAB = (int) WhiteCardinal, \r
1615     PM_WD = (int) WhiteDragon, \r
1616     PM_WL = (int) WhiteLance, \r
1617     PM_WS = (int) WhiteCobra, \r
1618     PM_WV = (int) WhiteFalcon, \r
1619     PM_WSG = (int) WhiteSilver, \r
1620     PM_WG = (int) WhiteGrasshopper, \r
1621     PM_WK = (int) WhiteKing,\r
1622     PM_BP = (int) BlackPawn, \r
1623     PM_BN = (int) BlackKnight, \r
1624     PM_BB = (int) BlackBishop, \r
1625     PM_BR = (int) BlackRook, \r
1626     PM_BQ = (int) BlackQueen, \r
1627     PM_BF = (int) BlackFerz, \r
1628     PM_BW = (int) BlackWazir, \r
1629     PM_BE = (int) BlackAlfil, \r
1630     PM_BM = (int) BlackMan,\r
1631     PM_BO = (int) BlackCannon, \r
1632     PM_BU = (int) BlackUnicorn, \r
1633     PM_BH = (int) BlackNightrider, \r
1634     PM_BA = (int) BlackAngel, \r
1635     PM_BC = (int) BlackMarshall, \r
1636     PM_BG = (int) BlackGrasshopper, \r
1637     PM_BAB = (int) BlackCardinal,\r
1638     PM_BD = (int) BlackDragon,\r
1639     PM_BL = (int) BlackLance,\r
1640     PM_BS = (int) BlackCobra,\r
1641     PM_BV = (int) BlackFalcon,\r
1642     PM_BSG = (int) BlackSilver,\r
1643     PM_BK = (int) BlackKing\r
1644 };\r
1645 \r
1646 static HFONT hPieceFont = NULL;\r
1647 static HBITMAP hPieceMask[(int) EmptySquare];\r
1648 static HBITMAP hPieceFace[(int) EmptySquare];\r
1649 static int fontBitmapSquareSize = 0;\r
1650 static char pieceToFontChar[(int) EmptySquare] =\r
1651                               { 'p', 'n', 'b', 'r', 'q', \r
1652                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1653                       'k', 'o', 'm', 'v', 't', 'w', \r
1654                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1655                                                               'l' };\r
1656 \r
1657 extern BOOL SetCharTable( char *table, const char * map );\r
1658 /* [HGM] moved to backend.c */\r
1659 \r
1660 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1661 {\r
1662     HBRUSH hbrush;\r
1663     BYTE r1 = GetRValue( color );\r
1664     BYTE g1 = GetGValue( color );\r
1665     BYTE b1 = GetBValue( color );\r
1666     BYTE r2 = r1 / 2;\r
1667     BYTE g2 = g1 / 2;\r
1668     BYTE b2 = b1 / 2;\r
1669     RECT rc;\r
1670 \r
1671     /* Create a uniform background first */\r
1672     hbrush = CreateSolidBrush( color );\r
1673     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1674     FillRect( hdc, &rc, hbrush );\r
1675     DeleteObject( hbrush );\r
1676     \r
1677     if( mode == 1 ) {\r
1678         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1679         int steps = squareSize / 2;\r
1680         int i;\r
1681 \r
1682         for( i=0; i<steps; i++ ) {\r
1683             BYTE r = r1 - (r1-r2) * i / steps;\r
1684             BYTE g = g1 - (g1-g2) * i / steps;\r
1685             BYTE b = b1 - (b1-b2) * i / steps;\r
1686 \r
1687             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1688             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1689             FillRect( hdc, &rc, hbrush );\r
1690             DeleteObject(hbrush);\r
1691         }\r
1692     }\r
1693     else if( mode == 2 ) {\r
1694         /* Diagonal gradient, good more or less for every piece */\r
1695         POINT triangle[3];\r
1696         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1697         HBRUSH hbrush_old;\r
1698         int steps = squareSize;\r
1699         int i;\r
1700 \r
1701         triangle[0].x = squareSize - steps;\r
1702         triangle[0].y = squareSize;\r
1703         triangle[1].x = squareSize;\r
1704         triangle[1].y = squareSize;\r
1705         triangle[2].x = squareSize;\r
1706         triangle[2].y = squareSize - steps;\r
1707 \r
1708         for( i=0; i<steps; i++ ) {\r
1709             BYTE r = r1 - (r1-r2) * i / steps;\r
1710             BYTE g = g1 - (g1-g2) * i / steps;\r
1711             BYTE b = b1 - (b1-b2) * i / steps;\r
1712 \r
1713             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1714             hbrush_old = SelectObject( hdc, hbrush );\r
1715             Polygon( hdc, triangle, 3 );\r
1716             SelectObject( hdc, hbrush_old );\r
1717             DeleteObject(hbrush);\r
1718             triangle[0].x++;\r
1719             triangle[2].y++;\r
1720         }\r
1721 \r
1722         SelectObject( hdc, hpen );\r
1723     }\r
1724 }\r
1725 \r
1726 /*\r
1727     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1728     seems to work ok. The main problem here is to find the "inside" of a chess\r
1729     piece: follow the steps as explained below.\r
1730 */\r
1731 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1732 {\r
1733     HBITMAP hbm;\r
1734     HBITMAP hbm_old;\r
1735     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1736     RECT rc;\r
1737     SIZE sz;\r
1738     POINT pt;\r
1739     int backColor = whitePieceColor; \r
1740     int foreColor = blackPieceColor;\r
1741     \r
1742     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1743         backColor = appData.fontBackColorWhite;\r
1744         foreColor = appData.fontForeColorWhite;\r
1745     }\r
1746     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1747         backColor = appData.fontBackColorBlack;\r
1748         foreColor = appData.fontForeColorBlack;\r
1749     }\r
1750 \r
1751     /* Mask */\r
1752     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1753 \r
1754     hbm_old = SelectObject( hdc, hbm );\r
1755 \r
1756     rc.left = 0;\r
1757     rc.top = 0;\r
1758     rc.right = squareSize;\r
1759     rc.bottom = squareSize;\r
1760 \r
1761     /* Step 1: background is now black */\r
1762     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1763 \r
1764     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1765 \r
1766     pt.x = (squareSize - sz.cx) / 2;\r
1767     pt.y = (squareSize - sz.cy) / 2;\r
1768 \r
1769     SetBkMode( hdc, TRANSPARENT );\r
1770     SetTextColor( hdc, chroma );\r
1771     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1772     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1773 \r
1774     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1775     /* Step 3: the area outside the piece is filled with white */\r
1776 //    FloodFill( hdc, 0, 0, chroma );\r
1777     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1778     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1779     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1780     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1781     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1782     /* \r
1783         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1784         but if the start point is not inside the piece we're lost!\r
1785         There should be a better way to do this... if we could create a region or path\r
1786         from the fill operation we would be fine for example.\r
1787     */\r
1788 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1789     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1790 \r
1791     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1792         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1793         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1794 \r
1795         SelectObject( dc2, bm2 );\r
1796         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1797         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1798         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1799         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1800         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1801 \r
1802         DeleteDC( dc2 );\r
1803         DeleteObject( bm2 );\r
1804     }\r
1805 \r
1806     SetTextColor( hdc, 0 );\r
1807     /* \r
1808         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1809         draw the piece again in black for safety.\r
1810     */\r
1811     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1812 \r
1813     SelectObject( hdc, hbm_old );\r
1814 \r
1815     if( hPieceMask[index] != NULL ) {\r
1816         DeleteObject( hPieceMask[index] );\r
1817     }\r
1818 \r
1819     hPieceMask[index] = hbm;\r
1820 \r
1821     /* Face */\r
1822     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1823 \r
1824     SelectObject( hdc, hbm );\r
1825 \r
1826     {\r
1827         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1828         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1829         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1830 \r
1831         SelectObject( dc1, hPieceMask[index] );\r
1832         SelectObject( dc2, bm2 );\r
1833         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1834         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1835         \r
1836         /* \r
1837             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1838             the piece background and deletes (makes transparent) the rest.\r
1839             Thanks to that mask, we are free to paint the background with the greates\r
1840             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1841             We use this, to make gradients and give the pieces a "roundish" look.\r
1842         */\r
1843         SetPieceBackground( hdc, backColor, 2 );\r
1844         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1845 \r
1846         DeleteDC( dc2 );\r
1847         DeleteDC( dc1 );\r
1848         DeleteObject( bm2 );\r
1849     }\r
1850 \r
1851     SetTextColor( hdc, foreColor );\r
1852     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1853 \r
1854     SelectObject( hdc, hbm_old );\r
1855 \r
1856     if( hPieceFace[index] != NULL ) {\r
1857         DeleteObject( hPieceFace[index] );\r
1858     }\r
1859 \r
1860     hPieceFace[index] = hbm;\r
1861 }\r
1862 \r
1863 static int TranslatePieceToFontPiece( int piece )\r
1864 {\r
1865     switch( piece ) {\r
1866     case BlackPawn:\r
1867         return PM_BP;\r
1868     case BlackKnight:\r
1869         return PM_BN;\r
1870     case BlackBishop:\r
1871         return PM_BB;\r
1872     case BlackRook:\r
1873         return PM_BR;\r
1874     case BlackQueen:\r
1875         return PM_BQ;\r
1876     case BlackKing:\r
1877         return PM_BK;\r
1878     case WhitePawn:\r
1879         return PM_WP;\r
1880     case WhiteKnight:\r
1881         return PM_WN;\r
1882     case WhiteBishop:\r
1883         return PM_WB;\r
1884     case WhiteRook:\r
1885         return PM_WR;\r
1886     case WhiteQueen:\r
1887         return PM_WQ;\r
1888     case WhiteKing:\r
1889         return PM_WK;\r
1890 \r
1891     case BlackAngel:\r
1892         return PM_BA;\r
1893     case BlackMarshall:\r
1894         return PM_BC;\r
1895     case BlackFerz:\r
1896         return PM_BF;\r
1897     case BlackNightrider:\r
1898         return PM_BH;\r
1899     case BlackAlfil:\r
1900         return PM_BE;\r
1901     case BlackWazir:\r
1902         return PM_BW;\r
1903     case BlackUnicorn:\r
1904         return PM_BU;\r
1905     case BlackCannon:\r
1906         return PM_BO;\r
1907     case BlackGrasshopper:\r
1908         return PM_BG;\r
1909     case BlackMan:\r
1910         return PM_BM;\r
1911     case BlackSilver:\r
1912         return PM_BSG;\r
1913     case BlackLance:\r
1914         return PM_BL;\r
1915     case BlackFalcon:\r
1916         return PM_BV;\r
1917     case BlackCobra:\r
1918         return PM_BS;\r
1919     case BlackCardinal:\r
1920         return PM_BAB;\r
1921     case BlackDragon:\r
1922         return PM_BD;\r
1923 \r
1924     case WhiteAngel:\r
1925         return PM_WA;\r
1926     case WhiteMarshall:\r
1927         return PM_WC;\r
1928     case WhiteFerz:\r
1929         return PM_WF;\r
1930     case WhiteNightrider:\r
1931         return PM_WH;\r
1932     case WhiteAlfil:\r
1933         return PM_WE;\r
1934     case WhiteWazir:\r
1935         return PM_WW;\r
1936     case WhiteUnicorn:\r
1937         return PM_WU;\r
1938     case WhiteCannon:\r
1939         return PM_WO;\r
1940     case WhiteGrasshopper:\r
1941         return PM_WG;\r
1942     case WhiteMan:\r
1943         return PM_WM;\r
1944     case WhiteSilver:\r
1945         return PM_WSG;\r
1946     case WhiteLance:\r
1947         return PM_WL;\r
1948     case WhiteFalcon:\r
1949         return PM_WV;\r
1950     case WhiteCobra:\r
1951         return PM_WS;\r
1952     case WhiteCardinal:\r
1953         return PM_WAB;\r
1954     case WhiteDragon:\r
1955         return PM_WD;\r
1956     }\r
1957 \r
1958     return 0;\r
1959 }\r
1960 \r
1961 void CreatePiecesFromFont()\r
1962 {\r
1963     LOGFONT lf;\r
1964     HDC hdc_window = NULL;\r
1965     HDC hdc = NULL;\r
1966     HFONT hfont_old;\r
1967     int fontHeight;\r
1968     int i;\r
1969 \r
1970     if( fontBitmapSquareSize < 0 ) {\r
1971         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1972         return;\r
1973     }\r
1974 \r
1975     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1976         fontBitmapSquareSize = -1;\r
1977         return;\r
1978     }\r
1979 \r
1980     if( fontBitmapSquareSize != squareSize ) {\r
1981         hdc_window = GetDC( hwndMain );\r
1982         hdc = CreateCompatibleDC( hdc_window );\r
1983 \r
1984         if( hPieceFont != NULL ) {\r
1985             DeleteObject( hPieceFont );\r
1986         }\r
1987         else {\r
1988             for( i=0; i<=(int)BlackKing; i++ ) {\r
1989                 hPieceMask[i] = NULL;\r
1990                 hPieceFace[i] = NULL;\r
1991             }\r
1992         }\r
1993 \r
1994         fontHeight = 75;\r
1995 \r
1996         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1997             fontHeight = appData.fontPieceSize;\r
1998         }\r
1999 \r
2000         fontHeight = (fontHeight * squareSize) / 100;\r
2001 \r
2002         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2003         lf.lfWidth = 0;\r
2004         lf.lfEscapement = 0;\r
2005         lf.lfOrientation = 0;\r
2006         lf.lfWeight = FW_NORMAL;\r
2007         lf.lfItalic = 0;\r
2008         lf.lfUnderline = 0;\r
2009         lf.lfStrikeOut = 0;\r
2010         lf.lfCharSet = DEFAULT_CHARSET;\r
2011         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2012         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2013         lf.lfQuality = PROOF_QUALITY;\r
2014         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2015         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2016         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2017 \r
2018         hPieceFont = CreateFontIndirect( &lf );\r
2019 \r
2020         if( hPieceFont == NULL ) {\r
2021             fontBitmapSquareSize = -2;\r
2022         }\r
2023         else {\r
2024             /* Setup font-to-piece character table */\r
2025             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2026                 /* No (or wrong) global settings, try to detect the font */\r
2027                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2028                     /* Alpha */\r
2029                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2030                 }\r
2031                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2032                     /* DiagramTT* family */\r
2033                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2034                 }\r
2035                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2036                     /* Fairy symbols */\r
2037                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2038                 }\r
2039                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2040                     /* Good Companion (Some characters get warped as literal :-( */\r
2041                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2042                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2043                     SetCharTable(pieceToFontChar, s);\r
2044                 }\r
2045                 else {\r
2046                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2047                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2048                 }\r
2049             }\r
2050 \r
2051             /* Create bitmaps */\r
2052             hfont_old = SelectObject( hdc, hPieceFont );\r
2053             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2054                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2055                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2056 \r
2057             SelectObject( hdc, hfont_old );\r
2058 \r
2059             fontBitmapSquareSize = squareSize;\r
2060         }\r
2061     }\r
2062 \r
2063     if( hdc != NULL ) {\r
2064         DeleteDC( hdc );\r
2065     }\r
2066 \r
2067     if( hdc_window != NULL ) {\r
2068         ReleaseDC( hwndMain, hdc_window );\r
2069     }\r
2070 }\r
2071 \r
2072 HBITMAP\r
2073 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2074 {\r
2075   char name[128];\r
2076 \r
2077     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2078   if (gameInfo.event &&\r
2079       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2080       strcmp(name, "k80s") == 0) {\r
2081     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2082   }\r
2083   return LoadBitmap(hinst, name);\r
2084 }\r
2085 \r
2086 \r
2087 /* Insert a color into the program's logical palette\r
2088    structure.  This code assumes the given color is\r
2089    the result of the RGB or PALETTERGB macro, and it\r
2090    knows how those macros work (which is documented).\r
2091 */\r
2092 VOID\r
2093 InsertInPalette(COLORREF color)\r
2094 {\r
2095   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2096 \r
2097   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2098     DisplayFatalError(_("Too many colors"), 0, 1);\r
2099     pLogPal->palNumEntries--;\r
2100     return;\r
2101   }\r
2102 \r
2103   pe->peFlags = (char) 0;\r
2104   pe->peRed = (char) (0xFF & color);\r
2105   pe->peGreen = (char) (0xFF & (color >> 8));\r
2106   pe->peBlue = (char) (0xFF & (color >> 16));\r
2107   return;\r
2108 }\r
2109 \r
2110 \r
2111 VOID\r
2112 InitDrawingColors()\r
2113 {\r
2114   if (pLogPal == NULL) {\r
2115     /* Allocate enough memory for a logical palette with\r
2116      * PALETTESIZE entries and set the size and version fields\r
2117      * of the logical palette structure.\r
2118      */\r
2119     pLogPal = (NPLOGPALETTE)\r
2120       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2121                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2122     pLogPal->palVersion    = 0x300;\r
2123   }\r
2124   pLogPal->palNumEntries = 0;\r
2125 \r
2126   InsertInPalette(lightSquareColor);\r
2127   InsertInPalette(darkSquareColor);\r
2128   InsertInPalette(whitePieceColor);\r
2129   InsertInPalette(blackPieceColor);\r
2130   InsertInPalette(highlightSquareColor);\r
2131   InsertInPalette(premoveHighlightColor);\r
2132 \r
2133   /*  create a logical color palette according the information\r
2134    *  in the LOGPALETTE structure.\r
2135    */\r
2136   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2137 \r
2138   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2139   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2140   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2141   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2142   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2143   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2144   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2145   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2146   /* [AS] Force rendering of the font-based pieces */\r
2147   if( fontBitmapSquareSize > 0 ) {\r
2148     fontBitmapSquareSize = 0;\r
2149   }\r
2150 }\r
2151 \r
2152 \r
2153 int\r
2154 BoardWidth(int boardSize, int n)\r
2155 { /* [HGM] argument n added to allow different width and height */\r
2156   int lineGap = sizeInfo[boardSize].lineGap;\r
2157 \r
2158   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2159       lineGap = appData.overrideLineGap;\r
2160   }\r
2161 \r
2162   return (n + 1) * lineGap +\r
2163           n * sizeInfo[boardSize].squareSize;\r
2164 }\r
2165 \r
2166 /* Respond to board resize by dragging edge */\r
2167 VOID\r
2168 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2169 {\r
2170   BoardSize newSize = NUM_SIZES - 1;\r
2171   static int recurse = 0;\r
2172   if (IsIconic(hwndMain)) return;\r
2173   if (recurse > 0) return;\r
2174   recurse++;\r
2175   while (newSize > 0) {\r
2176         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2177         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2178            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2179     newSize--;\r
2180   } \r
2181   boardSize = newSize;\r
2182   InitDrawingSizes(boardSize, flags);\r
2183   recurse--;\r
2184 }\r
2185 \r
2186 \r
2187 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2188 \r
2189 VOID\r
2190 InitDrawingSizes(BoardSize boardSize, int flags)\r
2191 {\r
2192   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2193   ChessSquare piece;\r
2194   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2195   HDC hdc;\r
2196   SIZE clockSize, messageSize;\r
2197   HFONT oldFont;\r
2198   char buf[MSG_SIZ];\r
2199   char *str;\r
2200   HMENU hmenu = GetMenu(hwndMain);\r
2201   RECT crect, wrect, oldRect;\r
2202   int offby;\r
2203   LOGBRUSH logbrush;\r
2204 \r
2205   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2206   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2207 \r
2208   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2209   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2210 \r
2211   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2212   oldRect.top = wpMain.y;\r
2213   oldRect.right = wpMain.x + wpMain.width;\r
2214   oldRect.bottom = wpMain.y + wpMain.height;\r
2215 \r
2216   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2217   smallLayout = sizeInfo[boardSize].smallLayout;\r
2218   squareSize = sizeInfo[boardSize].squareSize;\r
2219   lineGap = sizeInfo[boardSize].lineGap;\r
2220   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2221 \r
2222   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2223       lineGap = appData.overrideLineGap;\r
2224   }\r
2225 \r
2226   if (tinyLayout != oldTinyLayout) {\r
2227     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2228     if (tinyLayout) {\r
2229       style &= ~WS_SYSMENU;\r
2230       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2231                  "&Minimize\tCtrl+F4");\r
2232     } else {\r
2233       style |= WS_SYSMENU;\r
2234       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2235     }\r
2236     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2237 \r
2238     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2239       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2240         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2241     }\r
2242     DrawMenuBar(hwndMain);\r
2243   }\r
2244 \r
2245   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2246   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2247 \r
2248   /* Get text area sizes */\r
2249   hdc = GetDC(hwndMain);\r
2250   if (appData.clockMode) {\r
2251     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2252   } else {\r
2253     snprintf(buf, MSG_SIZ, _("White"));\r
2254   }\r
2255   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2256   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2257   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2258   str = _("We only care about the height here");\r
2259   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2260   SelectObject(hdc, oldFont);\r
2261   ReleaseDC(hwndMain, hdc);\r
2262 \r
2263   /* Compute where everything goes */\r
2264   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2265         /* [HGM] logo: if either logo is on, reserve space for it */\r
2266         logoHeight =  2*clockSize.cy;\r
2267         leftLogoRect.left   = OUTER_MARGIN;\r
2268         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2269         leftLogoRect.top    = OUTER_MARGIN;\r
2270         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2271 \r
2272         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2273         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2274         rightLogoRect.top    = OUTER_MARGIN;\r
2275         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2276 \r
2277 \r
2278     whiteRect.left = leftLogoRect.right;\r
2279     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2280     whiteRect.top = OUTER_MARGIN;\r
2281     whiteRect.bottom = whiteRect.top + logoHeight;\r
2282 \r
2283     blackRect.right = rightLogoRect.left;\r
2284     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2285     blackRect.top = whiteRect.top;\r
2286     blackRect.bottom = whiteRect.bottom;\r
2287   } else {\r
2288     whiteRect.left = OUTER_MARGIN;\r
2289     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2290     whiteRect.top = OUTER_MARGIN;\r
2291     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2292 \r
2293     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2294     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2295     blackRect.top = whiteRect.top;\r
2296     blackRect.bottom = whiteRect.bottom;\r
2297 \r
2298     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2299   }\r
2300 \r
2301   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2302   if (appData.showButtonBar) {\r
2303     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2304       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2305   } else {\r
2306     messageRect.right = OUTER_MARGIN + boardWidth;\r
2307   }\r
2308   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2309   messageRect.bottom = messageRect.top + messageSize.cy;\r
2310 \r
2311   boardRect.left = OUTER_MARGIN;\r
2312   boardRect.right = boardRect.left + boardWidth;\r
2313   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2314   boardRect.bottom = boardRect.top + boardHeight;\r
2315 \r
2316   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2317   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2318   oldBoardSize = boardSize;\r
2319   oldTinyLayout = tinyLayout;\r
2320   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2321   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2322     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2323   winW *= 1 + twoBoards;\r
2324   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2325   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2326   wpMain.height = winH; //       without disturbing window attachments\r
2327   GetWindowRect(hwndMain, &wrect);\r
2328   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2329                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2330 \r
2331   // [HGM] placement: let attached windows follow size change.\r
2332   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2333   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2334   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2335   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2336   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2337 \r
2338   /* compensate if menu bar wrapped */\r
2339   GetClientRect(hwndMain, &crect);\r
2340   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2341   wpMain.height += offby;\r
2342   switch (flags) {\r
2343   case WMSZ_TOPLEFT:\r
2344     SetWindowPos(hwndMain, NULL, \r
2345                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2346                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2347     break;\r
2348 \r
2349   case WMSZ_TOPRIGHT:\r
2350   case WMSZ_TOP:\r
2351     SetWindowPos(hwndMain, NULL, \r
2352                  wrect.left, wrect.bottom - wpMain.height, \r
2353                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2354     break;\r
2355 \r
2356   case WMSZ_BOTTOMLEFT:\r
2357   case WMSZ_LEFT:\r
2358     SetWindowPos(hwndMain, NULL, \r
2359                  wrect.right - wpMain.width, wrect.top, \r
2360                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2361     break;\r
2362 \r
2363   case WMSZ_BOTTOMRIGHT:\r
2364   case WMSZ_BOTTOM:\r
2365   case WMSZ_RIGHT:\r
2366   default:\r
2367     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2368                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2369     break;\r
2370   }\r
2371 \r
2372   hwndPause = NULL;\r
2373   for (i = 0; i < N_BUTTONS; i++) {\r
2374     if (buttonDesc[i].hwnd != NULL) {\r
2375       DestroyWindow(buttonDesc[i].hwnd);\r
2376       buttonDesc[i].hwnd = NULL;\r
2377     }\r
2378     if (appData.showButtonBar) {\r
2379       buttonDesc[i].hwnd =\r
2380         CreateWindow("BUTTON", buttonDesc[i].label,\r
2381                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2382                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2383                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2384                      (HMENU) buttonDesc[i].id,\r
2385                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2386       if (tinyLayout) {\r
2387         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2388                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2389                     MAKELPARAM(FALSE, 0));\r
2390       }\r
2391       if (buttonDesc[i].id == IDM_Pause)\r
2392         hwndPause = buttonDesc[i].hwnd;\r
2393       buttonDesc[i].wndproc = (WNDPROC)\r
2394         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2395     }\r
2396   }\r
2397   if (gridPen != NULL) DeleteObject(gridPen);\r
2398   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2399   if (premovePen != NULL) DeleteObject(premovePen);\r
2400   if (lineGap != 0) {\r
2401     logbrush.lbStyle = BS_SOLID;\r
2402     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2403     gridPen =\r
2404       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2405                    lineGap, &logbrush, 0, NULL);\r
2406     logbrush.lbColor = highlightSquareColor;\r
2407     highlightPen =\r
2408       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2409                    lineGap, &logbrush, 0, NULL);\r
2410 \r
2411     logbrush.lbColor = premoveHighlightColor; \r
2412     premovePen =\r
2413       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2414                    lineGap, &logbrush, 0, NULL);\r
2415 \r
2416     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2417     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2418       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2419       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2420         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2421       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2422         BOARD_WIDTH * (squareSize + lineGap);\r
2423       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2424     }\r
2425     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2426       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2427       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2428         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2429         lineGap / 2 + (i * (squareSize + lineGap));\r
2430       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2431         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2432       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2433     }\r
2434   }\r
2435 \r
2436   /* [HGM] Licensing requirement */\r
2437 #ifdef GOTHIC\r
2438   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2439 #endif\r
2440 #ifdef FALCON\r
2441   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2442 #endif\r
2443   GothicPopUp( "", VariantNormal);\r
2444 \r
2445 \r
2446 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2447 \r
2448   /* Load piece bitmaps for this board size */\r
2449   for (i=0; i<=2; i++) {\r
2450     for (piece = WhitePawn;\r
2451          (int) piece < (int) BlackPawn;\r
2452          piece = (ChessSquare) ((int) piece + 1)) {\r
2453       if (pieceBitmap[i][piece] != NULL)\r
2454         DeleteObject(pieceBitmap[i][piece]);\r
2455     }\r
2456   }\r
2457 \r
2458   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2459   // Orthodox Chess pieces\r
2460   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2461   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2462   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2463   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2464   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2465   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2466   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2467   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2468   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2469   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2470   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2471   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2472   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2473   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2474   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2475   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2476     // in Shogi, Hijack the unused Queen for Lance\r
2477     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2478     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2479     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2480   } else {\r
2481     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2482     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2483     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2484   }\r
2485 \r
2486   if(squareSize <= 72 && squareSize >= 33) { \r
2487     /* A & C are available in most sizes now */\r
2488     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2489       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2490       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2491       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2492       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2493       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2494       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2495       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2496       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2497       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2498       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2499       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2500       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2501     } else { // Smirf-like\r
2502       if(gameInfo.variant == VariantSChess) {\r
2503         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2504         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2505         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2506       } else {\r
2507         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2508         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2509         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2510       }\r
2511     }\r
2512     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2513       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2514       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2515       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2516     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2517       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2518       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2519       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2520     } else { // WinBoard standard\r
2521       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2522       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2523       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2524     }\r
2525   }\r
2526 \r
2527 \r
2528   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2529     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2530     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2531     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2532     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2533     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2534     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2535     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2536     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2537     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2538     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2539     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2540     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2541     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2542     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2543     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2544     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2545     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2546     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2547     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2548     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2549     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2550     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2551     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2552     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2553     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2554     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2555     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2556     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2557     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2558     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2559 \r
2560     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2561       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2562       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2563       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2564       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2565       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2566       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2567       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2568       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2569       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2570       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2571       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2572       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2573     } else {\r
2574       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2575       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2576       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2577       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2578       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2579       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2580       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2581       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2582       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2583       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2584       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2585       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2586     }\r
2587 \r
2588   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2589     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2590     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2591     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2592     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2593     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2594     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2595     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2596     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2597     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2598     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2599     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2600     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2601     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2602     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2603   }\r
2604 \r
2605 \r
2606   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2607   /* special Shogi support in this size */\r
2608   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2609       for (piece = WhitePawn;\r
2610            (int) piece < (int) BlackPawn;\r
2611            piece = (ChessSquare) ((int) piece + 1)) {\r
2612         if (pieceBitmap[i][piece] != NULL)\r
2613           DeleteObject(pieceBitmap[i][piece]);\r
2614       }\r
2615     }\r
2616   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2617   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2618   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2619   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2620   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2621   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2622   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2623   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2624   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2625   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2626   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2627   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2628   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2629   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2630   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2631   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2632   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2633   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2634   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2635   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2636   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2637   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2638   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2639   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2640   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2641   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2642   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2643   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2644   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2645   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2646   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2647   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2648   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2649   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2650   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2651   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2652   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2653   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2654   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2655   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2656   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2657   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2658   minorSize = 0;\r
2659   }\r
2660 }\r
2661 \r
2662 HBITMAP\r
2663 PieceBitmap(ChessSquare p, int kind)\r
2664 {\r
2665   if ((int) p >= (int) BlackPawn)\r
2666     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2667 \r
2668   return pieceBitmap[kind][(int) p];\r
2669 }\r
2670 \r
2671 /***************************************************************/\r
2672 \r
2673 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2674 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2675 /*\r
2676 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2677 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2678 */\r
2679 \r
2680 VOID\r
2681 SquareToPos(int row, int column, int * x, int * y)\r
2682 {\r
2683   if (flipView) {\r
2684     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2685     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2686   } else {\r
2687     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2688     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2689   }\r
2690 }\r
2691 \r
2692 VOID\r
2693 DrawCoordsOnDC(HDC hdc)\r
2694 {\r
2695   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
2696   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
2697   char str[2] = { NULLCHAR, NULLCHAR };\r
2698   int oldMode, oldAlign, x, y, start, i;\r
2699   HFONT oldFont;\r
2700   HBRUSH oldBrush;\r
2701 \r
2702   if (!appData.showCoords)\r
2703     return;\r
2704 \r
2705   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2706 \r
2707   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2708   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2709   oldAlign = GetTextAlign(hdc);\r
2710   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2711 \r
2712   y = boardRect.top + lineGap;\r
2713   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2714 \r
2715   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2716   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2717     str[0] = files[start + i];\r
2718     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2719     y += squareSize + lineGap;\r
2720   }\r
2721 \r
2722   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2723 \r
2724   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2725   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2726     str[0] = ranks[start + i];\r
2727     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2728     x += squareSize + lineGap;\r
2729   }    \r
2730 \r
2731   SelectObject(hdc, oldBrush);\r
2732   SetBkMode(hdc, oldMode);\r
2733   SetTextAlign(hdc, oldAlign);\r
2734   SelectObject(hdc, oldFont);\r
2735 }\r
2736 \r
2737 VOID\r
2738 DrawGridOnDC(HDC hdc)\r
2739 {\r
2740   HPEN oldPen;\r
2741  \r
2742   if (lineGap != 0) {\r
2743     oldPen = SelectObject(hdc, gridPen);\r
2744     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2745     SelectObject(hdc, oldPen);\r
2746   }\r
2747 }\r
2748 \r
2749 #define HIGHLIGHT_PEN 0\r
2750 #define PREMOVE_PEN   1\r
2751 \r
2752 VOID\r
2753 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2754 {\r
2755   int x1, y1;\r
2756   HPEN oldPen, hPen;\r
2757   if (lineGap == 0) return;\r
2758   if (flipView) {\r
2759     x1 = boardRect.left +\r
2760       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2761     y1 = boardRect.top +\r
2762       lineGap/2 + y * (squareSize + lineGap);\r
2763   } else {\r
2764     x1 = boardRect.left +\r
2765       lineGap/2 + x * (squareSize + lineGap);\r
2766     y1 = boardRect.top +\r
2767       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2768   }\r
2769   hPen = pen ? premovePen : highlightPen;\r
2770   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2771   MoveToEx(hdc, x1, y1, NULL);\r
2772   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2773   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2774   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2775   LineTo(hdc, x1, y1);\r
2776   SelectObject(hdc, oldPen);\r
2777 }\r
2778 \r
2779 VOID\r
2780 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2781 {\r
2782   int i;\r
2783   for (i=0; i<2; i++) {\r
2784     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2785       DrawHighlightOnDC(hdc, TRUE,\r
2786                         h->sq[i].x, h->sq[i].y,\r
2787                         pen);\r
2788   }\r
2789 }\r
2790 \r
2791 /* Note: sqcolor is used only in monoMode */\r
2792 /* Note that this code is largely duplicated in woptions.c,\r
2793    function DrawSampleSquare, so that needs to be updated too */\r
2794 VOID\r
2795 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2796 {\r
2797   HBITMAP oldBitmap;\r
2798   HBRUSH oldBrush;\r
2799   int tmpSize;\r
2800 \r
2801   if (appData.blindfold) return;\r
2802 \r
2803   /* [AS] Use font-based pieces if needed */\r
2804   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2805     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2806     CreatePiecesFromFont();\r
2807 \r
2808     if( fontBitmapSquareSize == squareSize ) {\r
2809         int index = TranslatePieceToFontPiece(piece);\r
2810 \r
2811         SelectObject( tmphdc, hPieceMask[ index ] );\r
2812 \r
2813       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2814         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2815       else\r
2816         BitBlt( hdc,\r
2817             x, y,\r
2818             squareSize, squareSize,\r
2819             tmphdc,\r
2820             0, 0,\r
2821             SRCAND );\r
2822 \r
2823         SelectObject( tmphdc, hPieceFace[ index ] );\r
2824 \r
2825       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2826         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2827       else\r
2828         BitBlt( hdc,\r
2829             x, y,\r
2830             squareSize, squareSize,\r
2831             tmphdc,\r
2832             0, 0,\r
2833             SRCPAINT );\r
2834 \r
2835         return;\r
2836     }\r
2837   }\r
2838 \r
2839   if (appData.monoMode) {\r
2840     SelectObject(tmphdc, PieceBitmap(piece, \r
2841       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2842     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2843            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2844   } else {\r
2845     tmpSize = squareSize;\r
2846     if(minorSize &&\r
2847         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2848          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2849       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2850       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2851       x += (squareSize - minorSize)>>1;\r
2852       y += squareSize - minorSize - 2;\r
2853       tmpSize = minorSize;\r
2854     }\r
2855     if (color || appData.allWhite ) {\r
2856       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2857       if( color )\r
2858               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2859       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2860       if(appData.upsideDown && color==flipView)\r
2861         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2862       else\r
2863         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2864       /* Use black for outline of white pieces */\r
2865       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2866       if(appData.upsideDown && color==flipView)\r
2867         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2868       else\r
2869         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2870     } else {\r
2871       /* Use square color for details of black pieces */\r
2872       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2873       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2874       if(appData.upsideDown && !flipView)\r
2875         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2876       else\r
2877         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2878     }\r
2879     SelectObject(hdc, oldBrush);\r
2880     SelectObject(tmphdc, oldBitmap);\r
2881   }\r
2882 }\r
2883 \r
2884 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2885 int GetBackTextureMode( int algo )\r
2886 {\r
2887     int result = BACK_TEXTURE_MODE_DISABLED;\r
2888 \r
2889     switch( algo ) \r
2890     {\r
2891         case BACK_TEXTURE_MODE_PLAIN:\r
2892             result = 1; /* Always use identity map */\r
2893             break;\r
2894         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2895             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2896             break;\r
2897     }\r
2898 \r
2899     return result;\r
2900 }\r
2901 \r
2902 /* \r
2903     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2904     to handle redraws cleanly (as random numbers would always be different).\r
2905 */\r
2906 VOID RebuildTextureSquareInfo()\r
2907 {\r
2908     BITMAP bi;\r
2909     int lite_w = 0;\r
2910     int lite_h = 0;\r
2911     int dark_w = 0;\r
2912     int dark_h = 0;\r
2913     int row;\r
2914     int col;\r
2915 \r
2916     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2917 \r
2918     if( liteBackTexture != NULL ) {\r
2919         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2920             lite_w = bi.bmWidth;\r
2921             lite_h = bi.bmHeight;\r
2922         }\r
2923     }\r
2924 \r
2925     if( darkBackTexture != NULL ) {\r
2926         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2927             dark_w = bi.bmWidth;\r
2928             dark_h = bi.bmHeight;\r
2929         }\r
2930     }\r
2931 \r
2932     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2933         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2934             if( (col + row) & 1 ) {\r
2935                 /* Lite square */\r
2936                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2937                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2938                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2939                   else\r
2940                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2941                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2942                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2943                   else\r
2944                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2945                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2946                 }\r
2947             }\r
2948             else {\r
2949                 /* Dark square */\r
2950                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2951                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2952                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2953                   else\r
2954                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2955                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2956                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2957                   else\r
2958                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2959                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2960                 }\r
2961             }\r
2962         }\r
2963     }\r
2964 }\r
2965 \r
2966 /* [AS] Arrow highlighting support */\r
2967 \r
2968 static double A_WIDTH = 5; /* Width of arrow body */\r
2969 \r
2970 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2971 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2972 \r
2973 static double Sqr( double x )\r
2974 {\r
2975     return x*x;\r
2976 }\r
2977 \r
2978 static int Round( double x )\r
2979 {\r
2980     return (int) (x + 0.5);\r
2981 }\r
2982 \r
2983 /* Draw an arrow between two points using current settings */\r
2984 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2985 {\r
2986     POINT arrow[7];\r
2987     double dx, dy, j, k, x, y;\r
2988 \r
2989     if( d_x == s_x ) {\r
2990         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2991 \r
2992         arrow[0].x = s_x + A_WIDTH + 0.5;\r
2993         arrow[0].y = s_y;\r
2994 \r
2995         arrow[1].x = s_x + A_WIDTH + 0.5;\r
2996         arrow[1].y = d_y - h;\r
2997 \r
2998         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2999         arrow[2].y = d_y - h;\r
3000 \r
3001         arrow[3].x = d_x;\r
3002         arrow[3].y = d_y;\r
3003 \r
3004         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3005         arrow[5].y = d_y - h;\r
3006 \r
3007         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3008         arrow[4].y = d_y - h;\r
3009 \r
3010         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3011         arrow[6].y = s_y;\r
3012     }\r
3013     else if( d_y == s_y ) {\r
3014         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3015 \r
3016         arrow[0].x = s_x;\r
3017         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3018 \r
3019         arrow[1].x = d_x - w;\r
3020         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3021 \r
3022         arrow[2].x = d_x - w;\r
3023         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3024 \r
3025         arrow[3].x = d_x;\r
3026         arrow[3].y = d_y;\r
3027 \r
3028         arrow[5].x = d_x - w;\r
3029         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3030 \r
3031         arrow[4].x = d_x - w;\r
3032         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3033 \r
3034         arrow[6].x = s_x;\r
3035         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3036     }\r
3037     else {\r
3038         /* [AS] Needed a lot of paper for this! :-) */\r
3039         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3040         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3041   \r
3042         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3043 \r
3044         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3045 \r
3046         x = s_x;\r
3047         y = s_y;\r
3048 \r
3049         arrow[0].x = Round(x - j);\r
3050         arrow[0].y = Round(y + j*dx);\r
3051 \r
3052         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3053         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3054 \r
3055         if( d_x > s_x ) {\r
3056             x = (double) d_x - k;\r
3057             y = (double) d_y - k*dy;\r
3058         }\r
3059         else {\r
3060             x = (double) d_x + k;\r
3061             y = (double) d_y + k*dy;\r
3062         }\r
3063 \r
3064         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3065 \r
3066         arrow[6].x = Round(x - j);\r
3067         arrow[6].y = Round(y + j*dx);\r
3068 \r
3069         arrow[2].x = Round(arrow[6].x + 2*j);\r
3070         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3071 \r
3072         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3073         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3074 \r
3075         arrow[4].x = d_x;\r
3076         arrow[4].y = d_y;\r
3077 \r
3078         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3079         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3080     }\r
3081 \r
3082     Polygon( hdc, arrow, 7 );\r
3083 }\r
3084 \r
3085 /* [AS] Draw an arrow between two squares */\r
3086 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3087 {\r
3088     int s_x, s_y, d_x, d_y;\r
3089     HPEN hpen;\r
3090     HPEN holdpen;\r
3091     HBRUSH hbrush;\r
3092     HBRUSH holdbrush;\r
3093     LOGBRUSH stLB;\r
3094 \r
3095     if( s_col == d_col && s_row == d_row ) {\r
3096         return;\r
3097     }\r
3098 \r
3099     /* Get source and destination points */\r
3100     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3101     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3102 \r
3103     if( d_y > s_y ) {\r
3104         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3105     }\r
3106     else if( d_y < s_y ) {\r
3107         d_y += squareSize / 2 + squareSize / 4;\r
3108     }\r
3109     else {\r
3110         d_y += squareSize / 2;\r
3111     }\r
3112 \r
3113     if( d_x > s_x ) {\r
3114         d_x += squareSize / 2 - squareSize / 4;\r
3115     }\r
3116     else if( d_x < s_x ) {\r
3117         d_x += squareSize / 2 + squareSize / 4;\r
3118     }\r
3119     else {\r
3120         d_x += squareSize / 2;\r
3121     }\r
3122 \r
3123     s_x += squareSize / 2;\r
3124     s_y += squareSize / 2;\r
3125 \r
3126     /* Adjust width */\r
3127     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3128 \r
3129     /* Draw */\r
3130     stLB.lbStyle = BS_SOLID;\r
3131     stLB.lbColor = appData.highlightArrowColor;\r
3132     stLB.lbHatch = 0;\r
3133 \r
3134     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3135     holdpen = SelectObject( hdc, hpen );\r
3136     hbrush = CreateBrushIndirect( &stLB );\r
3137     holdbrush = SelectObject( hdc, hbrush );\r
3138 \r
3139     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3140 \r
3141     SelectObject( hdc, holdpen );\r
3142     SelectObject( hdc, holdbrush );\r
3143     DeleteObject( hpen );\r
3144     DeleteObject( hbrush );\r
3145 }\r
3146 \r
3147 BOOL HasHighlightInfo()\r
3148 {\r
3149     BOOL result = FALSE;\r
3150 \r
3151     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3152         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3153     {\r
3154         result = TRUE;\r
3155     }\r
3156 \r
3157     return result;\r
3158 }\r
3159 \r
3160 BOOL IsDrawArrowEnabled()\r
3161 {\r
3162     BOOL result = FALSE;\r
3163 \r
3164     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3165         result = TRUE;\r
3166     }\r
3167 \r
3168     return result;\r
3169 }\r
3170 \r
3171 VOID DrawArrowHighlight( HDC hdc )\r
3172 {\r
3173     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3174         DrawArrowBetweenSquares( hdc,\r
3175             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3176             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3177     }\r
3178 }\r
3179 \r
3180 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3181 {\r
3182     HRGN result = NULL;\r
3183 \r
3184     if( HasHighlightInfo() ) {\r
3185         int x1, y1, x2, y2;\r
3186         int sx, sy, dx, dy;\r
3187 \r
3188         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3189         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3190 \r
3191         sx = MIN( x1, x2 );\r
3192         sy = MIN( y1, y2 );\r
3193         dx = MAX( x1, x2 ) + squareSize;\r
3194         dy = MAX( y1, y2 ) + squareSize;\r
3195 \r
3196         result = CreateRectRgn( sx, sy, dx, dy );\r
3197     }\r
3198 \r
3199     return result;\r
3200 }\r
3201 \r
3202 /*\r
3203     Warning: this function modifies the behavior of several other functions. \r
3204     \r
3205     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3206     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3207     repaint is scattered all over the place, which is not good for features such as\r
3208     "arrow highlighting" that require a full repaint of the board.\r
3209 \r
3210     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3211     user interaction, when speed is not so important) but especially to avoid errors\r
3212     in the displayed graphics.\r
3213 \r
3214     In such patched places, I always try refer to this function so there is a single\r
3215     place to maintain knowledge.\r
3216     \r
3217     To restore the original behavior, just return FALSE unconditionally.\r
3218 */\r
3219 BOOL IsFullRepaintPreferrable()\r
3220 {\r
3221     BOOL result = FALSE;\r
3222 \r
3223     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3224         /* Arrow may appear on the board */\r
3225         result = TRUE;\r
3226     }\r
3227 \r
3228     return result;\r
3229 }\r
3230 \r
3231 /* \r
3232     This function is called by DrawPosition to know whether a full repaint must\r
3233     be forced or not.\r
3234 \r
3235     Only DrawPosition may directly call this function, which makes use of \r
3236     some state information. Other function should call DrawPosition specifying \r
3237     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3238 */\r
3239 BOOL DrawPositionNeedsFullRepaint()\r
3240 {\r
3241     BOOL result = FALSE;\r
3242 \r
3243     /* \r
3244         Probably a slightly better policy would be to trigger a full repaint\r
3245         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3246         but animation is fast enough that it's difficult to notice.\r
3247     */\r
3248     if( animInfo.piece == EmptySquare ) {\r
3249         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3250             result = TRUE;\r
3251         }\r
3252     }\r
3253 \r
3254     return result;\r
3255 }\r
3256 \r
3257 VOID\r
3258 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3259 {\r
3260   int row, column, x, y, square_color, piece_color;\r
3261   ChessSquare piece;\r
3262   HBRUSH oldBrush;\r
3263   HDC texture_hdc = NULL;\r
3264 \r
3265   /* [AS] Initialize background textures if needed */\r
3266   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3267       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3268       if( backTextureSquareSize != squareSize \r
3269        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3270           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3271           backTextureSquareSize = squareSize;\r
3272           RebuildTextureSquareInfo();\r
3273       }\r
3274 \r
3275       texture_hdc = CreateCompatibleDC( hdc );\r
3276   }\r
3277 \r
3278   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3279     for (column = 0; column < BOARD_WIDTH; column++) {\r
3280   \r
3281       SquareToPos(row, column, &x, &y);\r
3282 \r
3283       piece = board[row][column];\r
3284 \r
3285       square_color = ((column + row) % 2) == 1;\r
3286       if( gameInfo.variant == VariantXiangqi ) {\r
3287           square_color = !InPalace(row, column);\r
3288           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3289           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3290       }\r
3291       piece_color = (int) piece < (int) BlackPawn;\r
3292 \r
3293 \r
3294       /* [HGM] holdings file: light square or black */\r
3295       if(column == BOARD_LEFT-2) {\r
3296             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3297                 square_color = 1;\r
3298             else {\r
3299                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3300                 continue;\r
3301             }\r
3302       } else\r
3303       if(column == BOARD_RGHT + 1 ) {\r
3304             if( row < gameInfo.holdingsSize )\r
3305                 square_color = 1;\r
3306             else {\r
3307                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3308                 continue;\r
3309             }\r
3310       }\r
3311       if(column == BOARD_LEFT-1 ) /* left align */\r
3312             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3313       else if( column == BOARD_RGHT) /* right align */\r
3314             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3315       else\r
3316       if (appData.monoMode) {\r
3317         if (piece == EmptySquare) {\r
3318           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3319                  square_color ? WHITENESS : BLACKNESS);\r
3320         } else {\r
3321           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3322         }\r
3323       } \r
3324       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3325           /* [AS] Draw the square using a texture bitmap */\r
3326           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3327           int r = row, c = column; // [HGM] do not flip board in flipView\r
3328           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3329 \r
3330           DrawTile( x, y, \r
3331               squareSize, squareSize, \r
3332               hdc, \r
3333               texture_hdc,\r
3334               backTextureSquareInfo[r][c].mode,\r
3335               backTextureSquareInfo[r][c].x,\r
3336               backTextureSquareInfo[r][c].y );\r
3337 \r
3338           SelectObject( texture_hdc, hbm );\r
3339 \r
3340           if (piece != EmptySquare) {\r
3341               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3342           }\r
3343       }\r
3344       else {\r
3345         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3346 \r
3347         oldBrush = SelectObject(hdc, brush );\r
3348         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3349         SelectObject(hdc, oldBrush);\r
3350         if (piece != EmptySquare)\r
3351           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3352       }\r
3353     }\r
3354   }\r
3355 \r
3356   if( texture_hdc != NULL ) {\r
3357     DeleteDC( texture_hdc );\r
3358   }\r
3359 }\r
3360 \r
3361 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3362 void fputDW(FILE *f, int x)\r
3363 {\r
3364         fputc(x     & 255, f);\r
3365         fputc(x>>8  & 255, f);\r
3366         fputc(x>>16 & 255, f);\r
3367         fputc(x>>24 & 255, f);\r
3368 }\r
3369 \r
3370 #define MAX_CLIPS 200   /* more than enough */\r
3371 \r
3372 VOID\r
3373 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3374 {\r
3375 //  HBITMAP bufferBitmap;\r
3376   BITMAP bi;\r
3377 //  RECT Rect;\r
3378   HDC tmphdc;\r
3379   HBITMAP hbm;\r
3380   int w = 100, h = 50;\r
3381 \r
3382   if(logo == NULL) {\r
3383     if(!logoHeight) return;\r
3384     FillRect( hdc, &logoRect, whitePieceBrush );\r
3385   }\r
3386 //  GetClientRect(hwndMain, &Rect);\r
3387 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3388 //                                      Rect.bottom-Rect.top+1);\r
3389   tmphdc = CreateCompatibleDC(hdc);\r
3390   hbm = SelectObject(tmphdc, logo);\r
3391   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3392             w = bi.bmWidth;\r
3393             h = bi.bmHeight;\r
3394   }\r
3395   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3396                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3397   SelectObject(tmphdc, hbm);\r
3398   DeleteDC(tmphdc);\r
3399 }\r
3400 \r
3401 VOID\r
3402 DisplayLogos()\r
3403 {\r
3404   if(logoHeight) {\r
3405         HDC hdc = GetDC(hwndMain);\r
3406         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3407         if(appData.autoLogo) {\r
3408           \r
3409           switch(gameMode) { // pick logos based on game mode\r
3410             case IcsObserving:\r
3411                 whiteLogo = second.programLogo; // ICS logo\r
3412                 blackLogo = second.programLogo;\r
3413             default:\r
3414                 break;\r
3415             case IcsPlayingWhite:\r
3416                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3417                 blackLogo = second.programLogo; // ICS logo\r
3418                 break;\r
3419             case IcsPlayingBlack:\r
3420                 whiteLogo = second.programLogo; // ICS logo\r
3421                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3422                 break;\r
3423             case TwoMachinesPlay:\r
3424                 if(first.twoMachinesColor[0] == 'b') {\r
3425                     whiteLogo = second.programLogo;\r
3426                     blackLogo = first.programLogo;\r
3427                 }\r
3428                 break;\r
3429             case MachinePlaysWhite:\r
3430                 blackLogo = userLogo;\r
3431                 break;\r
3432             case MachinePlaysBlack:\r
3433                 whiteLogo = userLogo;\r
3434                 blackLogo = first.programLogo;\r
3435           }\r
3436         }\r
3437         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3438         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3439         ReleaseDC(hwndMain, hdc);\r
3440   }\r
3441 }\r
3442 \r
3443 void\r
3444 UpdateLogos(int display)\r
3445 { // called after loading new engine(s), in tourney or from menu\r
3446   LoadLogo(&first, 0, FALSE);\r
3447   LoadLogo(&second, 1, appData.icsActive);\r
3448   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3449   if(display) DisplayLogos();\r
3450 }\r
3451 \r
3452 static HDC hdcSeek;\r
3453 \r
3454 // [HGM] seekgraph\r
3455 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3456 {\r
3457     POINT stPt;\r
3458     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3459     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3460     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3461     SelectObject( hdcSeek, hp );\r
3462 }\r
3463 \r
3464 // front-end wrapper for drawing functions to do rectangles\r
3465 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3466 {\r
3467     HPEN hp;\r
3468     RECT rc;\r
3469 \r
3470     if (hdcSeek == NULL) {\r
3471     hdcSeek = GetDC(hwndMain);\r
3472       if (!appData.monoMode) {\r
3473         SelectPalette(hdcSeek, hPal, FALSE);\r
3474         RealizePalette(hdcSeek);\r
3475       }\r
3476     }\r
3477     hp = SelectObject( hdcSeek, gridPen );\r
3478     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3479     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3480     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3481     SelectObject( hdcSeek, hp );\r
3482 }\r
3483 \r
3484 // front-end wrapper for putting text in graph\r
3485 void DrawSeekText(char *buf, int x, int y)\r
3486 {\r
3487         SIZE stSize;\r
3488         SetBkMode( hdcSeek, TRANSPARENT );\r
3489         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3490         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3491 }\r
3492 \r
3493 void DrawSeekDot(int x, int y, int color)\r
3494 {\r
3495         int square = color & 0x80;\r
3496         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3497                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3498         color &= 0x7F;\r
3499         if(square)\r
3500             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3501                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3502         else\r
3503             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3504                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3505             SelectObject(hdcSeek, oldBrush);\r
3506 }\r
3507 \r
3508 VOID\r
3509 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3510 {\r
3511   static Board lastReq[2], lastDrawn[2];\r
3512   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3513   static int lastDrawnFlipView = 0;\r
3514   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3515   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3516   HDC tmphdc;\r
3517   HDC hdcmem;\r
3518   HBITMAP bufferBitmap;\r
3519   HBITMAP oldBitmap;\r
3520   RECT Rect;\r
3521   HRGN clips[MAX_CLIPS];\r
3522   ChessSquare dragged_piece = EmptySquare;\r
3523   int nr = twoBoards*partnerUp;\r
3524 \r
3525   /* I'm undecided on this - this function figures out whether a full\r
3526    * repaint is necessary on its own, so there's no real reason to have the\r
3527    * caller tell it that.  I think this can safely be set to FALSE - but\r
3528    * if we trust the callers not to request full repaints unnessesarily, then\r
3529    * we could skip some clipping work.  In other words, only request a full\r
3530    * redraw when the majority of pieces have changed positions (ie. flip, \r
3531    * gamestart and similar)  --Hawk\r
3532    */\r
3533   Boolean fullrepaint = repaint;\r
3534 \r
3535   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3536 \r
3537   if( DrawPositionNeedsFullRepaint() ) {\r
3538       fullrepaint = TRUE;\r
3539   }\r
3540 \r
3541   if (board == NULL) {\r
3542     if (!lastReqValid[nr]) {\r
3543       return;\r
3544     }\r
3545     board = lastReq[nr];\r
3546   } else {\r
3547     CopyBoard(lastReq[nr], board);\r
3548     lastReqValid[nr] = 1;\r
3549   }\r
3550 \r
3551   if (doingSizing) {\r
3552     return;\r
3553   }\r
3554 \r
3555   if (IsIconic(hwndMain)) {\r
3556     return;\r
3557   }\r
3558 \r
3559   if (hdc == NULL) {\r
3560     hdc = GetDC(hwndMain);\r
3561     if (!appData.monoMode) {\r
3562       SelectPalette(hdc, hPal, FALSE);\r
3563       RealizePalette(hdc);\r
3564     }\r
3565     releaseDC = TRUE;\r
3566   } else {\r
3567     releaseDC = FALSE;\r
3568   }\r
3569 \r
3570   /* Create some work-DCs */\r
3571   hdcmem = CreateCompatibleDC(hdc);\r
3572   tmphdc = CreateCompatibleDC(hdc);\r
3573 \r
3574   /* If dragging is in progress, we temporarely remove the piece */\r
3575   /* [HGM] or temporarily decrease count if stacked              */\r
3576   /*       !! Moved to before board compare !!                   */\r
3577   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3578     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3579     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3580             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3581         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3582     } else \r
3583     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3584             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3585         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3586     } else \r
3587         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3588   }\r
3589 \r
3590   /* Figure out which squares need updating by comparing the \r
3591    * newest board with the last drawn board and checking if\r
3592    * flipping has changed.\r
3593    */\r
3594   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3595     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3596       for (column = 0; column < BOARD_WIDTH; column++) {\r
3597         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3598           SquareToPos(row, column, &x, &y);\r
3599           clips[num_clips++] =\r
3600             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3601         }\r
3602       }\r
3603     }\r
3604    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3605     for (i=0; i<2; i++) {\r
3606       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3607           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3608         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3609             lastDrawnHighlight.sq[i].y >= 0) {\r
3610           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3611                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3612           clips[num_clips++] =\r
3613             CreateRectRgn(x - lineGap, y - lineGap, \r
3614                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3615         }\r
3616         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3617           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3618           clips[num_clips++] =\r
3619             CreateRectRgn(x - lineGap, y - lineGap, \r
3620                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3621         }\r
3622       }\r
3623     }\r
3624     for (i=0; i<2; i++) {\r
3625       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3626           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3627         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3628             lastDrawnPremove.sq[i].y >= 0) {\r
3629           SquareToPos(lastDrawnPremove.sq[i].y,\r
3630                       lastDrawnPremove.sq[i].x, &x, &y);\r
3631           clips[num_clips++] =\r
3632             CreateRectRgn(x - lineGap, y - lineGap, \r
3633                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3634         }\r
3635         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3636             premoveHighlightInfo.sq[i].y >= 0) {\r
3637           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3638                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3639           clips[num_clips++] =\r
3640             CreateRectRgn(x - lineGap, y - lineGap, \r
3641                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3642         }\r
3643       }\r
3644     }\r
3645    } else { // nr == 1\r
3646         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3647         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3648         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3649         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3650       for (i=0; i<2; i++) {\r
3651         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3652             partnerHighlightInfo.sq[i].y >= 0) {\r
3653           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3654                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3655           clips[num_clips++] =\r
3656             CreateRectRgn(x - lineGap, y - lineGap, \r
3657                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3658         }\r
3659         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3660             oldPartnerHighlight.sq[i].y >= 0) {\r
3661           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3662                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3663           clips[num_clips++] =\r
3664             CreateRectRgn(x - lineGap, y - lineGap, \r
3665                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3666         }\r
3667       }\r
3668    }\r
3669   } else {\r
3670     fullrepaint = TRUE;\r
3671   }\r
3672 \r
3673   /* Create a buffer bitmap - this is the actual bitmap\r
3674    * being written to.  When all the work is done, we can\r
3675    * copy it to the real DC (the screen).  This avoids\r
3676    * the problems with flickering.\r
3677    */\r
3678   GetClientRect(hwndMain, &Rect);\r
3679   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3680                                         Rect.bottom-Rect.top+1);\r
3681   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3682   if (!appData.monoMode) {\r
3683     SelectPalette(hdcmem, hPal, FALSE);\r
3684   }\r
3685 \r
3686   /* Create clips for dragging */\r
3687   if (!fullrepaint) {\r
3688     if (dragInfo.from.x >= 0) {\r
3689       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3690       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3691     }\r
3692     if (dragInfo.start.x >= 0) {\r
3693       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3694       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3695     }\r
3696     if (dragInfo.pos.x >= 0) {\r
3697       x = dragInfo.pos.x - squareSize / 2;\r
3698       y = dragInfo.pos.y - squareSize / 2;\r
3699       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3700     }\r
3701     if (dragInfo.lastpos.x >= 0) {\r
3702       x = dragInfo.lastpos.x - squareSize / 2;\r
3703       y = dragInfo.lastpos.y - squareSize / 2;\r
3704       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3705     }\r
3706   }\r
3707 \r
3708   /* Are we animating a move?  \r
3709    * If so, \r
3710    *   - remove the piece from the board (temporarely)\r
3711    *   - calculate the clipping region\r
3712    */\r
3713   if (!fullrepaint) {\r
3714     if (animInfo.piece != EmptySquare) {\r
3715       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3716       x = boardRect.left + animInfo.lastpos.x;\r
3717       y = boardRect.top + animInfo.lastpos.y;\r
3718       x2 = boardRect.left + animInfo.pos.x;\r
3719       y2 = boardRect.top + animInfo.pos.y;\r
3720       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3721       /* Slight kludge.  The real problem is that after AnimateMove is\r
3722          done, the position on the screen does not match lastDrawn.\r
3723          This currently causes trouble only on e.p. captures in\r
3724          atomic, where the piece moves to an empty square and then\r
3725          explodes.  The old and new positions both had an empty square\r
3726          at the destination, but animation has drawn a piece there and\r
3727          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3728       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3729     }\r
3730   }\r
3731 \r
3732   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3733   if (num_clips == 0)\r
3734     fullrepaint = TRUE;\r
3735 \r
3736   /* Set clipping on the memory DC */\r
3737   if (!fullrepaint) {\r
3738     SelectClipRgn(hdcmem, clips[0]);\r
3739     for (x = 1; x < num_clips; x++) {\r
3740       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3741         abort();  // this should never ever happen!\r
3742     }\r
3743   }\r
3744 \r
3745   /* Do all the drawing to the memory DC */\r
3746   if(explodeInfo.radius) { // [HGM] atomic\r
3747         HBRUSH oldBrush;\r
3748         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3749         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3750         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3751         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3752         x += squareSize/2;\r
3753         y += squareSize/2;\r
3754         if(!fullrepaint) {\r
3755           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3756           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3757         }\r
3758         DrawGridOnDC(hdcmem);\r
3759         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3760         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3761         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3762         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3763         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3764         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3765         SelectObject(hdcmem, oldBrush);\r
3766   } else {\r
3767     DrawGridOnDC(hdcmem);\r
3768     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3769         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3770         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3771     } else {\r
3772         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3773         oldPartnerHighlight = partnerHighlightInfo;\r
3774     }\r
3775     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3776   }\r
3777   if(nr == 0) // [HGM] dual: markers only on left board\r
3778   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3779     for (column = 0; column < BOARD_WIDTH; column++) {\r
3780         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3781             HBRUSH oldBrush = SelectObject(hdcmem, \r
3782                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3783             SquareToPos(row, column, &x, &y);\r
3784             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3785                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3786             SelectObject(hdcmem, oldBrush);\r
3787         }\r
3788     }\r
3789   }\r
3790 \r
3791   if( appData.highlightMoveWithArrow ) {\r
3792     DrawArrowHighlight(hdcmem);\r
3793   }\r
3794 \r
3795   DrawCoordsOnDC(hdcmem);\r
3796 \r
3797   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3798                  /* to make sure lastDrawn contains what is actually drawn */\r
3799 \r
3800   /* Put the dragged piece back into place and draw it (out of place!) */\r
3801     if (dragged_piece != EmptySquare) {\r
3802     /* [HGM] or restack */\r
3803     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3804                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3805     else\r
3806     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3807                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3808     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3809     x = dragInfo.pos.x - squareSize / 2;\r
3810     y = dragInfo.pos.y - squareSize / 2;\r
3811     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3812                   ((int) dragInfo.piece < (int) BlackPawn), \r
3813                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3814   }   \r
3815   \r
3816   /* Put the animated piece back into place and draw it */\r
3817   if (animInfo.piece != EmptySquare) {\r
3818     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3819     x = boardRect.left + animInfo.pos.x;\r
3820     y = boardRect.top + animInfo.pos.y;\r
3821     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3822                   ((int) animInfo.piece < (int) BlackPawn),\r
3823                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3824   }\r
3825 \r
3826   /* Release the bufferBitmap by selecting in the old bitmap \r
3827    * and delete the memory DC\r
3828    */\r
3829   SelectObject(hdcmem, oldBitmap);\r
3830   DeleteDC(hdcmem);\r
3831 \r
3832   /* Set clipping on the target DC */\r
3833   if (!fullrepaint) {\r
3834     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3835         RECT rect;\r
3836         GetRgnBox(clips[x], &rect);\r
3837         DeleteObject(clips[x]);\r
3838         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3839                           rect.right + wpMain.width/2, rect.bottom);\r
3840     }\r
3841     SelectClipRgn(hdc, clips[0]);\r
3842     for (x = 1; x < num_clips; x++) {\r
3843       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3844         abort();   // this should never ever happen!\r
3845     } \r
3846   }\r
3847 \r
3848   /* Copy the new bitmap onto the screen in one go.\r
3849    * This way we avoid any flickering\r
3850    */\r
3851   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3852   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3853          boardRect.right - boardRect.left,\r
3854          boardRect.bottom - boardRect.top,\r
3855          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3856   if(saveDiagFlag) { \r
3857     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3858     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3859 \r
3860     GetObject(bufferBitmap, sizeof(b), &b);\r
3861     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3862         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3863         bih.biWidth = b.bmWidth;\r
3864         bih.biHeight = b.bmHeight;\r
3865         bih.biPlanes = 1;\r
3866         bih.biBitCount = b.bmBitsPixel;\r
3867         bih.biCompression = 0;\r
3868         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3869         bih.biXPelsPerMeter = 0;\r
3870         bih.biYPelsPerMeter = 0;\r
3871         bih.biClrUsed = 0;\r
3872         bih.biClrImportant = 0;\r
3873 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3874 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3875         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3876 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3877 \r
3878         wb = b.bmWidthBytes;\r
3879         // count colors\r
3880         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3881                 int k = ((int*) pData)[i];\r
3882                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3883                 if(j >= 16) break;\r
3884                 color[j] = k;\r
3885                 if(j >= nrColors) nrColors = j+1;\r
3886         }\r
3887         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3888                 INT p = 0;\r
3889                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3890                     for(w=0; w<(wb>>2); w+=2) {\r
3891                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3892                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3893                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3894                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3895                         pData[p++] = m | j<<4;\r
3896                     }\r
3897                     while(p&3) pData[p++] = 0;\r
3898                 }\r
3899                 fac = 3;\r
3900                 wb = ((wb+31)>>5)<<2;\r
3901         }\r
3902         // write BITMAPFILEHEADER\r
3903         fprintf(diagFile, "BM");\r
3904         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3905         fputDW(diagFile, 0);\r
3906         fputDW(diagFile, 0x36 + (fac?64:0));\r
3907         // write BITMAPINFOHEADER\r
3908         fputDW(diagFile, 40);\r
3909         fputDW(diagFile, b.bmWidth);\r
3910         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3911         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3912         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3913         fputDW(diagFile, 0);\r
3914         fputDW(diagFile, 0);\r
3915         fputDW(diagFile, 0);\r
3916         fputDW(diagFile, 0);\r
3917         fputDW(diagFile, 0);\r
3918         fputDW(diagFile, 0);\r
3919         // write color table\r
3920         if(fac)\r
3921         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3922         // write bitmap data\r
3923         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3924                 fputc(pData[i], diagFile);\r
3925         free(pData);\r
3926      }\r
3927   }\r
3928 \r
3929   SelectObject(tmphdc, oldBitmap);\r
3930 \r
3931   /* Massive cleanup */\r
3932   for (x = 0; x < num_clips; x++)\r
3933     DeleteObject(clips[x]);\r
3934 \r
3935   DeleteDC(tmphdc);\r
3936   DeleteObject(bufferBitmap);\r
3937 \r
3938   if (releaseDC) \r
3939     ReleaseDC(hwndMain, hdc);\r
3940   \r
3941   if (lastDrawnFlipView != flipView && nr == 0) {\r
3942     if (flipView)\r
3943       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3944     else\r
3945       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3946   }\r
3947 \r
3948 /*  CopyBoard(lastDrawn, board);*/\r
3949   lastDrawnHighlight = highlightInfo;\r
3950   lastDrawnPremove   = premoveHighlightInfo;\r
3951   lastDrawnFlipView = flipView;\r
3952   lastDrawnValid[nr] = 1;\r
3953 }\r
3954 \r
3955 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3956 int\r
3957 SaveDiagram(f)\r
3958      FILE *f;\r
3959 {\r
3960     saveDiagFlag = 1; diagFile = f;\r
3961     HDCDrawPosition(NULL, TRUE, NULL);\r
3962     saveDiagFlag = 0;\r
3963 \r
3964     fclose(f);\r
3965     return TRUE;\r
3966 }\r
3967 \r
3968 \r
3969 /*---------------------------------------------------------------------------*\\r
3970 | CLIENT PAINT PROCEDURE\r
3971 |   This is the main event-handler for the WM_PAINT message.\r
3972 |\r
3973 \*---------------------------------------------------------------------------*/\r
3974 VOID\r
3975 PaintProc(HWND hwnd)\r
3976 {\r
3977   HDC         hdc;\r
3978   PAINTSTRUCT ps;\r
3979   HFONT       oldFont;\r
3980 \r
3981   if((hdc = BeginPaint(hwnd, &ps))) {\r
3982     if (IsIconic(hwnd)) {\r
3983       DrawIcon(hdc, 2, 2, iconCurrent);\r
3984     } else {\r
3985       if (!appData.monoMode) {\r
3986         SelectPalette(hdc, hPal, FALSE);\r
3987         RealizePalette(hdc);\r
3988       }\r
3989       HDCDrawPosition(hdc, 1, NULL);\r
3990       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3991         flipView = !flipView; partnerUp = !partnerUp;\r
3992         HDCDrawPosition(hdc, 1, NULL);\r
3993         flipView = !flipView; partnerUp = !partnerUp;\r
3994       }\r
3995       oldFont =\r
3996         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3997       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3998                  ETO_CLIPPED|ETO_OPAQUE,\r
3999                  &messageRect, messageText, strlen(messageText), NULL);\r
4000       SelectObject(hdc, oldFont);\r
4001       DisplayBothClocks();\r
4002       DisplayLogos();\r
4003     }\r
4004     EndPaint(hwnd,&ps);\r
4005   }\r
4006 \r
4007   return;\r
4008 }\r
4009 \r
4010 \r
4011 /*\r
4012  * If the user selects on a border boundary, return -1; if off the board,\r
4013  *   return -2.  Otherwise map the event coordinate to the square.\r
4014  * The offset boardRect.left or boardRect.top must already have been\r
4015  *   subtracted from x.\r
4016  */\r
4017 int EventToSquare(x, limit)\r
4018      int x, limit;\r
4019 {\r
4020   if (x <= 0)\r
4021     return -2;\r
4022   if (x < lineGap)\r
4023     return -1;\r
4024   x -= lineGap;\r
4025   if ((x % (squareSize + lineGap)) >= squareSize)\r
4026     return -1;\r
4027   x /= (squareSize + lineGap);\r
4028     if (x >= limit)\r
4029     return -2;\r
4030   return x;\r
4031 }\r
4032 \r
4033 typedef struct {\r
4034   char piece;\r
4035   int command;\r
4036   char* name;\r
4037 } DropEnable;\r
4038 \r
4039 DropEnable dropEnables[] = {\r
4040   { 'P', DP_Pawn, N_("Pawn") },\r
4041   { 'N', DP_Knight, N_("Knight") },\r
4042   { 'B', DP_Bishop, N_("Bishop") },\r
4043   { 'R', DP_Rook, N_("Rook") },\r
4044   { 'Q', DP_Queen, N_("Queen") },\r
4045 };\r
4046 \r
4047 VOID\r
4048 SetupDropMenu(HMENU hmenu)\r
4049 {\r
4050   int i, count, enable;\r
4051   char *p;\r
4052   extern char white_holding[], black_holding[];\r
4053   char item[MSG_SIZ];\r
4054 \r
4055   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4056     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4057                dropEnables[i].piece);\r
4058     count = 0;\r
4059     while (p && *p++ == dropEnables[i].piece) count++;\r
4060       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4061     enable = count > 0 || !appData.testLegality\r
4062       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4063                       && !appData.icsActive);\r
4064     ModifyMenu(hmenu, dropEnables[i].command,\r
4065                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4066                dropEnables[i].command, item);\r
4067   }\r
4068 }\r
4069 \r
4070 void DragPieceBegin(int x, int y)\r
4071 {\r
4072       dragInfo.lastpos.x = boardRect.left + x;\r
4073       dragInfo.lastpos.y = boardRect.top + y;\r
4074       dragInfo.from.x = fromX;\r
4075       dragInfo.from.y = fromY;\r
4076       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4077       dragInfo.start = dragInfo.from;\r
4078       SetCapture(hwndMain);\r
4079 }\r
4080 \r
4081 void DragPieceEnd(int x, int y)\r
4082 {\r
4083     ReleaseCapture();\r
4084     dragInfo.start.x = dragInfo.start.y = -1;\r
4085     dragInfo.from = dragInfo.start;\r
4086     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4087 }\r
4088 \r
4089 void ChangeDragPiece(ChessSquare piece)\r
4090 {\r
4091     dragInfo.piece = piece;\r
4092 }\r
4093 \r
4094 /* Event handler for mouse messages */\r
4095 VOID\r
4096 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4097 {\r
4098   int x, y, menuNr;\r
4099   POINT pt;\r
4100   static int recursive = 0;\r
4101   HMENU hmenu;\r
4102   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4103 \r
4104   if (recursive) {\r
4105     if (message == WM_MBUTTONUP) {\r
4106       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4107          to the middle button: we simulate pressing the left button too!\r
4108          */\r
4109       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4110       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4111     }\r
4112     return;\r
4113   }\r
4114   recursive++;\r
4115   \r
4116   pt.x = LOWORD(lParam);\r
4117   pt.y = HIWORD(lParam);\r
4118   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4119   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4120   if (!flipView && y >= 0) {\r
4121     y = BOARD_HEIGHT - 1 - y;\r
4122   }\r
4123   if (flipView && x >= 0) {\r
4124     x = BOARD_WIDTH - 1 - x;\r
4125   }\r
4126 \r
4127   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4128 \r
4129   switch (message) {\r
4130   case WM_LBUTTONDOWN:\r
4131       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4132         ClockClick(flipClock);\r
4133       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4134         ClockClick(!flipClock);\r
4135       }\r
4136       dragInfo.start.x = dragInfo.start.y = -1;\r
4137       dragInfo.from = dragInfo.start;\r
4138     if(fromX == -1 && frozen) { // not sure where this is for\r
4139                 fromX = fromY = -1; \r
4140       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4141       break;\r
4142     }\r
4143       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4144       DrawPosition(TRUE, NULL);\r
4145     break;\r
4146 \r
4147   case WM_LBUTTONUP:\r
4148       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4149       DrawPosition(TRUE, NULL);\r
4150     break;\r
4151 \r
4152   case WM_MOUSEMOVE:\r
4153     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4154     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4155     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4156     if ((appData.animateDragging || appData.highlightDragging)\r
4157         && (wParam & MK_LBUTTON)\r
4158         && dragInfo.from.x >= 0) \r
4159     {\r
4160       BOOL full_repaint = FALSE;\r
4161 \r
4162       if (appData.animateDragging) {\r
4163         dragInfo.pos = pt;\r
4164       }\r
4165       if (appData.highlightDragging) {\r
4166         SetHighlights(fromX, fromY, x, y);\r
4167         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4168             full_repaint = TRUE;\r
4169         }\r
4170       }\r
4171       \r
4172       DrawPosition( full_repaint, NULL);\r
4173       \r
4174       dragInfo.lastpos = dragInfo.pos;\r
4175     }\r
4176     break;\r
4177 \r
4178   case WM_MOUSEWHEEL: // [DM]\r
4179     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4180        /* Mouse Wheel is being rolled forward\r
4181         * Play moves forward\r
4182         */\r
4183        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4184                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4185        /* Mouse Wheel is being rolled backward\r
4186         * Play moves backward\r
4187         */\r
4188        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4189                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4190     }\r
4191     break;\r
4192 \r
4193   case WM_MBUTTONUP:\r
4194   case WM_RBUTTONUP:\r
4195     ReleaseCapture();\r
4196     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4197     break;\r
4198  \r
4199   case WM_MBUTTONDOWN:\r
4200   case WM_RBUTTONDOWN:\r
4201     ErrorPopDown();\r
4202     ReleaseCapture();\r
4203     fromX = fromY = -1;\r
4204     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4205     dragInfo.start.x = dragInfo.start.y = -1;\r
4206     dragInfo.from = dragInfo.start;\r
4207     dragInfo.lastpos = dragInfo.pos;\r
4208     if (appData.highlightDragging) {\r
4209       ClearHighlights();\r
4210     }\r
4211     if(y == -2) {\r
4212       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4213       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4214           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4215       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4216           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4217       }\r
4218       break;\r
4219     }\r
4220     DrawPosition(TRUE, NULL);\r
4221 \r
4222     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4223     switch (menuNr) {\r
4224     case 0:\r
4225       if (message == WM_MBUTTONDOWN) {\r
4226         buttonCount = 3;  /* even if system didn't think so */\r
4227         if (wParam & MK_SHIFT) \r
4228           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4229         else\r
4230           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4231       } else { /* message == WM_RBUTTONDOWN */\r
4232         /* Just have one menu, on the right button.  Windows users don't\r
4233            think to try the middle one, and sometimes other software steals\r
4234            it, or it doesn't really exist. */\r
4235         if(gameInfo.variant != VariantShogi)\r
4236             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4237         else\r
4238             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4239       }\r
4240       break;\r
4241     case 2:\r
4242       SetCapture(hwndMain);
4243       break;\r
4244     case 1:\r
4245       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4246       SetupDropMenu(hmenu);\r
4247       MenuPopup(hwnd, pt, hmenu, -1);\r
4248     default:\r
4249       break;\r
4250     }\r
4251     break;\r
4252   }\r
4253 \r
4254   recursive--;\r
4255 }\r
4256 \r
4257 /* Preprocess messages for buttons in main window */\r
4258 LRESULT CALLBACK\r
4259 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4260 {\r
4261   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4262   int i, dir;\r
4263 \r
4264   for (i=0; i<N_BUTTONS; i++) {\r
4265     if (buttonDesc[i].id == id) break;\r
4266   }\r
4267   if (i == N_BUTTONS) return 0;\r
4268   switch (message) {\r
4269   case WM_KEYDOWN:\r
4270     switch (wParam) {\r
4271     case VK_LEFT:\r
4272     case VK_RIGHT:\r
4273       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4274       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4275       return TRUE;\r
4276     }\r
4277     break;\r
4278   case WM_CHAR:\r
4279     switch (wParam) {\r
4280     case '\r':\r
4281       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4282       return TRUE;\r
4283     default:\r
4284       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4285         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4286         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4287         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4288         SetFocus(h);\r
4289         SendMessage(h, WM_CHAR, wParam, lParam);\r
4290         return TRUE;\r
4291       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4292         TypeInEvent((char)wParam);\r
4293       }\r
4294       break;\r
4295     }\r
4296     break;\r
4297   }\r
4298   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4299 }\r
4300 \r
4301 /* Process messages for Promotion dialog box */\r
4302 LRESULT CALLBACK\r
4303 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4304 {\r
4305   char promoChar;\r
4306 \r
4307   switch (message) {\r
4308   case WM_INITDIALOG: /* message: initialize dialog box */\r
4309     /* Center the dialog over the application window */\r
4310     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4311     Translate(hDlg, DLG_PromotionKing);\r
4312     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4313       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4314        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4315        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4316                SW_SHOW : SW_HIDE);\r
4317     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4318     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4319        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4320          PieceToChar(WhiteAngel) != '~') ||\r
4321         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4322          PieceToChar(BlackAngel) != '~')   ) ?\r
4323                SW_SHOW : SW_HIDE);\r
4324     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4325        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4326          PieceToChar(WhiteMarshall) != '~') ||\r
4327         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4328          PieceToChar(BlackMarshall) != '~')   ) ?\r
4329                SW_SHOW : SW_HIDE);\r
4330     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4331     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4332        gameInfo.variant != VariantShogi ?\r
4333                SW_SHOW : SW_HIDE);\r
4334     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4335        gameInfo.variant != VariantShogi ?\r
4336                SW_SHOW : SW_HIDE);\r
4337     if(gameInfo.variant == VariantShogi) {\r
4338         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4339         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4340         SetWindowText(hDlg, "Promote?");\r
4341     }\r
4342     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4343        gameInfo.variant == VariantSuper ?\r
4344                SW_SHOW : SW_HIDE);\r
4345     return TRUE;\r
4346 \r
4347   case WM_COMMAND: /* message: received a command */\r
4348     switch (LOWORD(wParam)) {\r
4349     case IDCANCEL:\r
4350       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4351       ClearHighlights();\r
4352       DrawPosition(FALSE, NULL);\r
4353       return TRUE;\r
4354     case PB_King:\r
4355       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4356       break;\r
4357     case PB_Queen:\r
4358       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4359       break;\r
4360     case PB_Rook:\r
4361       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4362       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4363       break;\r
4364     case PB_Bishop:\r
4365       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4366       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4367       break;\r
4368     case PB_Chancellor:\r
4369       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4370       break;\r
4371     case PB_Archbishop:\r
4372       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4373       break;\r
4374     case PB_Knight:\r
4375       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4376       break;\r
4377     default:\r
4378       return FALSE;\r
4379     }\r
4380     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4381     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4382     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4383     fromX = fromY = -1;\r
4384     if (!appData.highlightLastMove) {\r
4385       ClearHighlights();\r
4386       DrawPosition(FALSE, NULL);\r
4387     }\r
4388     return TRUE;\r
4389   }\r
4390   return FALSE;\r
4391 }\r
4392 \r
4393 /* Pop up promotion dialog */\r
4394 VOID\r
4395 PromotionPopup(HWND hwnd)\r
4396 {\r
4397   FARPROC lpProc;\r
4398 \r
4399   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4400   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4401     hwnd, (DLGPROC)lpProc);\r
4402   FreeProcInstance(lpProc);\r
4403 }\r
4404 \r
4405 void\r
4406 PromotionPopUp()\r
4407 {\r
4408   DrawPosition(TRUE, NULL);\r
4409   PromotionPopup(hwndMain);\r
4410 }\r
4411 \r
4412 /* Toggle ShowThinking */\r
4413 VOID\r
4414 ToggleShowThinking()\r
4415 {\r
4416   appData.showThinking = !appData.showThinking;\r
4417   ShowThinkingEvent();\r
4418 }\r
4419 \r
4420 VOID\r
4421 LoadGameDialog(HWND hwnd, char* title)\r
4422 {\r
4423   UINT number = 0;\r
4424   FILE *f;\r
4425   char fileTitle[MSG_SIZ];\r
4426   f = OpenFileDialog(hwnd, "rb", "",\r
4427                      appData.oldSaveStyle ? "gam" : "pgn",\r
4428                      GAME_FILT,\r
4429                      title, &number, fileTitle, NULL);\r
4430   if (f != NULL) {\r
4431     cmailMsgLoaded = FALSE;\r
4432     if (number == 0) {\r
4433       int error = GameListBuild(f);\r
4434       if (error) {\r
4435         DisplayError(_("Cannot build game list"), error);\r
4436       } else if (!ListEmpty(&gameList) &&\r
4437                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4438         GameListPopUp(f, fileTitle);\r
4439         return;\r
4440       }\r
4441       GameListDestroy();\r
4442       number = 1;\r
4443     }\r
4444     LoadGame(f, number, fileTitle, FALSE);\r
4445   }\r
4446 }\r
4447 \r
4448 int get_term_width()\r
4449 {\r
4450     HDC hdc;\r
4451     TEXTMETRIC tm;\r
4452     RECT rc;\r
4453     HFONT hfont, hold_font;\r
4454     LOGFONT lf;\r
4455     HWND hText;\r
4456 \r
4457     if (hwndConsole)\r
4458         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4459     else\r
4460         return 79;\r
4461 \r
4462     // get the text metrics\r
4463     hdc = GetDC(hText);\r
4464     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4465     if (consoleCF.dwEffects & CFE_BOLD)\r
4466         lf.lfWeight = FW_BOLD;\r
4467     if (consoleCF.dwEffects & CFE_ITALIC)\r
4468         lf.lfItalic = TRUE;\r
4469     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4470         lf.lfStrikeOut = TRUE;\r
4471     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4472         lf.lfUnderline = TRUE;\r
4473     hfont = CreateFontIndirect(&lf);\r
4474     hold_font = SelectObject(hdc, hfont);\r
4475     GetTextMetrics(hdc, &tm);\r
4476     SelectObject(hdc, hold_font);\r
4477     DeleteObject(hfont);\r
4478     ReleaseDC(hText, hdc);\r
4479 \r
4480     // get the rectangle\r
4481     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4482 \r
4483     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4484 }\r
4485 \r
4486 void UpdateICSWidth(HWND hText)\r
4487 {\r
4488     LONG old_width, new_width;\r
4489 \r
4490     new_width = get_term_width(hText, FALSE);\r
4491     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4492     if (new_width != old_width)\r
4493     {\r
4494         ics_update_width(new_width);\r
4495         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4496     }\r
4497 }\r
4498 \r
4499 VOID\r
4500 ChangedConsoleFont()\r
4501 {\r
4502   CHARFORMAT cfmt;\r
4503   CHARRANGE tmpsel, sel;\r
4504   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4505   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4506   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4507   PARAFORMAT paraf;\r
4508 \r
4509   cfmt.cbSize = sizeof(CHARFORMAT);\r
4510   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4511     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4512                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4513   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4514    * size.  This was undocumented in the version of MSVC++ that I had\r
4515    * when I wrote the code, but is apparently documented now.\r
4516    */\r
4517   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4518   cfmt.bCharSet = f->lf.lfCharSet;\r
4519   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4520   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4521   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4522   /* Why are the following seemingly needed too? */\r
4523   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4524   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4525   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4526   tmpsel.cpMin = 0;\r
4527   tmpsel.cpMax = -1; /*999999?*/\r
4528   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4529   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4530   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4531    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4532    */\r
4533   paraf.cbSize = sizeof(paraf);\r
4534   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4535   paraf.dxStartIndent = 0;\r
4536   paraf.dxOffset = WRAP_INDENT;\r
4537   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4538   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4539   UpdateICSWidth(hText);\r
4540 }\r
4541 \r
4542 /*---------------------------------------------------------------------------*\\r
4543  *\r
4544  * Window Proc for main window\r
4545  *\r
4546 \*---------------------------------------------------------------------------*/\r
4547 \r
4548 /* Process messages for main window, etc. */\r
4549 LRESULT CALLBACK\r
4550 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4551 {\r
4552   FARPROC lpProc;\r
4553   int wmId, wmEvent;\r
4554   char *defName;\r
4555   FILE *f;\r
4556   UINT number;\r
4557   char fileTitle[MSG_SIZ];\r
4558   char buf[MSG_SIZ];\r
4559   static SnapData sd;\r
4560 \r
4561   switch (message) {\r
4562 \r
4563   case WM_PAINT: /* message: repaint portion of window */\r
4564     PaintProc(hwnd);\r
4565     break;\r
4566 \r
4567   case WM_ERASEBKGND:\r
4568     if (IsIconic(hwnd)) {\r
4569       /* Cheat; change the message */\r
4570       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4571     } else {\r
4572       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4573     }\r
4574     break;\r
4575 \r
4576   case WM_LBUTTONDOWN:\r
4577   case WM_MBUTTONDOWN:\r
4578   case WM_RBUTTONDOWN:\r
4579   case WM_LBUTTONUP:\r
4580   case WM_MBUTTONUP:\r
4581   case WM_RBUTTONUP:\r
4582   case WM_MOUSEMOVE:\r
4583   case WM_MOUSEWHEEL:\r
4584     MouseEvent(hwnd, message, wParam, lParam);\r
4585     break;\r
4586 \r
4587   JAWS_KB_NAVIGATION\r
4588 \r
4589   case WM_CHAR:\r
4590     \r
4591     JAWS_ALT_INTERCEPT\r
4592 \r
4593     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4594         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4595         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4596         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4597         SetFocus(h);\r
4598         SendMessage(h, message, wParam, lParam);\r
4599     } else if(lParam != KF_REPEAT) {\r
4600         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4601                 TypeInEvent((char)wParam);\r
4602         } else if((char)wParam == 003) CopyGameToClipboard();\r
4603          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4604     }\r
4605 \r
4606     break;\r
4607 \r
4608   case WM_PALETTECHANGED:\r
4609     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4610       int nnew;\r
4611       HDC hdc = GetDC(hwndMain);\r
4612       SelectPalette(hdc, hPal, TRUE);\r
4613       nnew = RealizePalette(hdc);\r
4614       if (nnew > 0) {\r
4615         paletteChanged = TRUE;\r
4616         InvalidateRect(hwnd, &boardRect, FALSE);\r
4617       }\r
4618       ReleaseDC(hwnd, hdc);\r
4619     }\r
4620     break;\r
4621 \r
4622   case WM_QUERYNEWPALETTE:\r
4623     if (!appData.monoMode /*&& paletteChanged*/) {\r
4624       int nnew;\r
4625       HDC hdc = GetDC(hwndMain);\r
4626       paletteChanged = FALSE;\r
4627       SelectPalette(hdc, hPal, FALSE);\r
4628       nnew = RealizePalette(hdc);\r
4629       if (nnew > 0) {\r
4630         InvalidateRect(hwnd, &boardRect, FALSE);\r
4631       }\r
4632       ReleaseDC(hwnd, hdc);\r
4633       return TRUE;\r
4634     }\r
4635     return FALSE;\r
4636 \r
4637   case WM_COMMAND: /* message: command from application menu */\r
4638     wmId    = LOWORD(wParam);\r
4639     wmEvent = HIWORD(wParam);\r
4640 \r
4641     switch (wmId) {\r
4642     case IDM_NewGame:\r
4643       ResetGameEvent();\r
4644       SAY("new game enter a move to play against the computer with white");\r
4645       break;\r
4646 \r
4647     case IDM_NewGameFRC:\r
4648       if( NewGameFRC() == 0 ) {\r
4649         ResetGameEvent();\r
4650       }\r
4651       break;\r
4652 \r
4653     case IDM_NewVariant:\r
4654       NewVariantPopup(hwnd);\r
4655       break;\r
4656 \r
4657     case IDM_LoadGame:\r
4658       LoadGameDialog(hwnd, _("Load Game from File"));\r
4659       break;\r
4660 \r
4661     case IDM_LoadNextGame:\r
4662       ReloadGame(1);\r
4663       break;\r
4664 \r
4665     case IDM_LoadPrevGame:\r
4666       ReloadGame(-1);\r
4667       break;\r
4668 \r
4669     case IDM_ReloadGame:\r
4670       ReloadGame(0);\r
4671       break;\r
4672 \r
4673     case IDM_LoadPosition:\r
4674       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4675         Reset(FALSE, TRUE);\r
4676       }\r
4677       number = 1;\r
4678       f = OpenFileDialog(hwnd, "rb", "",\r
4679                          appData.oldSaveStyle ? "pos" : "fen",\r
4680                          POSITION_FILT,\r
4681                          _("Load Position from File"), &number, fileTitle, NULL);\r
4682       if (f != NULL) {\r
4683         LoadPosition(f, number, fileTitle);\r
4684       }\r
4685       break;\r
4686 \r
4687     case IDM_LoadNextPosition:\r
4688       ReloadPosition(1);\r
4689       break;\r
4690 \r
4691     case IDM_LoadPrevPosition:\r
4692       ReloadPosition(-1);\r
4693       break;\r
4694 \r
4695     case IDM_ReloadPosition:\r
4696       ReloadPosition(0);\r
4697       break;\r
4698 \r
4699     case IDM_SaveGame:\r
4700       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4701       f = OpenFileDialog(hwnd, "a", defName,\r
4702                          appData.oldSaveStyle ? "gam" : "pgn",\r
4703                          GAME_FILT,\r
4704                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4705       if (f != NULL) {\r
4706         SaveGame(f, 0, "");\r
4707       }\r
4708       break;\r
4709 \r
4710     case IDM_SavePosition:\r
4711       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4712       f = OpenFileDialog(hwnd, "a", defName,\r
4713                          appData.oldSaveStyle ? "pos" : "fen",\r
4714                          POSITION_FILT,\r
4715                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4716       if (f != NULL) {\r
4717         SavePosition(f, 0, "");\r
4718       }\r
4719       break;\r
4720 \r
4721     case IDM_SaveDiagram:\r
4722       defName = "diagram";\r
4723       f = OpenFileDialog(hwnd, "wb", defName,\r
4724                          "bmp",\r
4725                          DIAGRAM_FILT,\r
4726                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4727       if (f != NULL) {\r
4728         SaveDiagram(f);\r
4729       }\r
4730       break;\r
4731 \r
4732     case IDM_CopyGame:\r
4733       CopyGameToClipboard();\r
4734       break;\r
4735 \r
4736     case IDM_PasteGame:\r
4737       PasteGameFromClipboard();\r
4738       break;\r
4739 \r
4740     case IDM_CopyGameListToClipboard:\r
4741       CopyGameListToClipboard();\r
4742       break;\r
4743 \r
4744     /* [AS] Autodetect FEN or PGN data */\r
4745     case IDM_PasteAny:\r
4746       PasteGameOrFENFromClipboard();\r
4747       break;\r
4748 \r
4749     /* [AS] Move history */\r
4750     case IDM_ShowMoveHistory:\r
4751         if( MoveHistoryIsUp() ) {\r
4752             MoveHistoryPopDown();\r
4753         }\r
4754         else {\r
4755             MoveHistoryPopUp();\r
4756         }\r
4757         break;\r
4758 \r
4759     /* [AS] Eval graph */\r
4760     case IDM_ShowEvalGraph:\r
4761         if( EvalGraphIsUp() ) {\r
4762             EvalGraphPopDown();\r
4763         }\r
4764         else {\r
4765             EvalGraphPopUp();\r
4766             SetFocus(hwndMain);\r
4767         }\r
4768         break;\r
4769 \r
4770     /* [AS] Engine output */\r
4771     case IDM_ShowEngineOutput:\r
4772         if( EngineOutputIsUp() ) {\r
4773             EngineOutputPopDown();\r
4774         }\r
4775         else {\r
4776             EngineOutputPopUp();\r
4777         }\r
4778         break;\r
4779 \r
4780     /* [AS] User adjudication */\r
4781     case IDM_UserAdjudication_White:\r
4782         UserAdjudicationEvent( +1 );\r
4783         break;\r
4784 \r
4785     case IDM_UserAdjudication_Black:\r
4786         UserAdjudicationEvent( -1 );\r
4787         break;\r
4788 \r
4789     case IDM_UserAdjudication_Draw:\r
4790         UserAdjudicationEvent( 0 );\r
4791         break;\r
4792 \r
4793     /* [AS] Game list options dialog */\r
4794     case IDM_GameListOptions:\r
4795       GameListOptions();\r
4796       break;\r
4797 \r
4798     case IDM_NewChat:\r
4799       ChatPopUp(NULL);\r
4800       break;\r
4801 \r
4802     case IDM_CopyPosition:\r
4803       CopyFENToClipboard();\r
4804       break;\r
4805 \r
4806     case IDM_PastePosition:\r
4807       PasteFENFromClipboard();\r
4808       break;\r
4809 \r
4810     case IDM_MailMove:\r
4811       MailMoveEvent();\r
4812       break;\r
4813 \r
4814     case IDM_ReloadCMailMsg:\r
4815       Reset(TRUE, TRUE);\r
4816       ReloadCmailMsgEvent(FALSE);\r
4817       break;\r
4818 \r
4819     case IDM_Minimize:\r
4820       ShowWindow(hwnd, SW_MINIMIZE);\r
4821       break;\r
4822 \r
4823     case IDM_Exit:\r
4824       ExitEvent(0);\r
4825       break;\r
4826 \r
4827     case IDM_MachineWhite:\r
4828       MachineWhiteEvent();\r
4829       /*\r
4830        * refresh the tags dialog only if it's visible\r
4831        */\r
4832       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4833           char *tags;\r
4834           tags = PGNTags(&gameInfo);\r
4835           TagsPopUp(tags, CmailMsg());\r
4836           free(tags);\r
4837       }\r
4838       SAY("computer starts playing white");\r
4839       break;\r
4840 \r
4841     case IDM_MachineBlack:\r
4842       MachineBlackEvent();\r
4843       /*\r
4844        * refresh the tags dialog only if it's visible\r
4845        */\r
4846       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4847           char *tags;\r
4848           tags = PGNTags(&gameInfo);\r
4849           TagsPopUp(tags, CmailMsg());\r
4850           free(tags);\r
4851       }\r
4852       SAY("computer starts playing black");\r
4853       break;\r
4854 \r
4855     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4856       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4857       break;\r
4858 \r
4859     case IDM_TwoMachines:\r
4860       TwoMachinesEvent();\r
4861       /*\r
4862        * refresh the tags dialog only if it's visible\r
4863        */\r
4864       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4865           char *tags;\r
4866           tags = PGNTags(&gameInfo);\r
4867           TagsPopUp(tags, CmailMsg());\r
4868           free(tags);\r
4869       }\r
4870       SAY("computer starts playing both sides");\r
4871       break;\r
4872 \r
4873     case IDM_AnalysisMode:\r
4874       if (!first.analysisSupport) {\r
4875         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4876         DisplayError(buf, 0);\r
4877       } else {\r
4878         SAY("analyzing current position");\r
4879         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4880         if (appData.icsActive) {\r
4881                if (gameMode != IcsObserving) {\r
4882                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4883                        DisplayError(buf, 0);\r
4884                        /* secure check */\r
4885                        if (appData.icsEngineAnalyze) {\r
4886                                if (appData.debugMode) \r
4887                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4888                                ExitAnalyzeMode();\r
4889                                ModeHighlight();\r
4890                                break;\r
4891                        }\r
4892                        break;\r
4893                } else {\r
4894                        /* if enable, user want disable icsEngineAnalyze */\r
4895                        if (appData.icsEngineAnalyze) {\r
4896                                ExitAnalyzeMode();\r
4897                                ModeHighlight();\r
4898                                break;\r
4899                        }\r
4900                        appData.icsEngineAnalyze = TRUE;\r
4901                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4902                }\r
4903         } \r
4904         if (!appData.showThinking) ToggleShowThinking();\r
4905         AnalyzeModeEvent();\r
4906       }\r
4907       break;\r
4908 \r
4909     case IDM_AnalyzeFile:\r
4910       if (!first.analysisSupport) {\r
4911         char buf[MSG_SIZ];\r
4912           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4913         DisplayError(buf, 0);\r
4914       } else {\r
4915         if (!appData.showThinking) ToggleShowThinking();\r
4916         AnalyzeFileEvent();\r
4917         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4918         AnalysisPeriodicEvent(1);\r
4919       }\r
4920       break;\r
4921 \r
4922     case IDM_IcsClient:\r
4923       IcsClientEvent();\r
4924       break;\r
4925 \r
4926     case IDM_EditGame:\r
4927     case IDM_EditGame2:\r
4928       EditGameEvent();\r
4929       SAY("edit game");\r
4930       break;\r
4931 \r
4932     case IDM_EditPosition:\r
4933     case IDM_EditPosition2:\r
4934       EditPositionEvent();\r
4935       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4936       break;\r
4937 \r
4938     case IDM_Training:\r
4939       TrainingEvent();\r
4940       break;\r
4941 \r
4942     case IDM_ShowGameList:\r
4943       ShowGameListProc();\r
4944       break;\r
4945 \r
4946     case IDM_EditProgs1:\r
4947       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4948       break;\r
4949 \r
4950     case IDM_EditProgs2:\r
4951      LoadEnginePopUp(hwndMain);\r
4952 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4953       break;\r
4954 \r
4955     case IDM_EditServers:\r
4956       EditTagsPopUp(icsNames, &icsNames);\r
4957       break;\r
4958 \r
4959     case IDM_EditTags:\r
4960     case IDM_Tags:\r
4961       EditTagsProc();\r
4962       break;\r
4963 \r
4964     case IDM_EditBook:\r
4965       EditBookEvent();\r
4966       break;\r
4967 \r
4968     case IDM_EditComment:\r
4969     case IDM_Comment:\r
4970       if (commentUp && editComment) {\r
4971         CommentPopDown();\r
4972       } else {\r
4973         EditCommentEvent();\r
4974       }\r
4975       break;\r
4976 \r
4977     case IDM_Pause:\r
4978       PauseEvent();\r
4979       break;\r
4980 \r
4981     case IDM_Accept:\r
4982       AcceptEvent();\r
4983       break;\r
4984 \r
4985     case IDM_Decline:\r
4986       DeclineEvent();\r
4987       break;\r
4988 \r
4989     case IDM_Rematch:\r
4990       RematchEvent();\r
4991       break;\r
4992 \r
4993     case IDM_CallFlag:\r
4994       CallFlagEvent();\r
4995       break;\r
4996 \r
4997     case IDM_Draw:\r
4998       DrawEvent();\r
4999       break;\r
5000 \r
5001     case IDM_Adjourn:\r
5002       AdjournEvent();\r
5003       break;\r
5004 \r
5005     case IDM_Abort:\r
5006       AbortEvent();\r
5007       break;\r
5008 \r
5009     case IDM_Resign:\r
5010       ResignEvent();\r
5011       break;\r
5012 \r
5013     case IDM_StopObserving:\r
5014       StopObservingEvent();\r
5015       break;\r
5016 \r
5017     case IDM_StopExamining:\r
5018       StopExaminingEvent();\r
5019       break;\r
5020 \r
5021     case IDM_Upload:\r
5022       UploadGameEvent();\r
5023       break;\r
5024 \r
5025     case IDM_TypeInMove:\r
5026       TypeInEvent('\000');\r
5027       break;\r
5028 \r
5029     case IDM_TypeInName:\r
5030       PopUpNameDialog('\000');\r
5031       break;\r
5032 \r
5033     case IDM_Backward:\r
5034       BackwardEvent();\r
5035       SetFocus(hwndMain);\r
5036       break;\r
5037 \r
5038     JAWS_MENU_ITEMS\r
5039 \r
5040     case IDM_Forward:\r
5041       ForwardEvent();\r
5042       SetFocus(hwndMain);\r
5043       break;\r
5044 \r
5045     case IDM_ToStart:\r
5046       ToStartEvent();\r
5047       SetFocus(hwndMain);\r
5048       break;\r
5049 \r
5050     case IDM_ToEnd:\r
5051       ToEndEvent();\r
5052       SetFocus(hwndMain);\r
5053       break;\r
5054 \r
5055     case IDM_Revert:\r
5056       RevertEvent(FALSE);\r
5057       break;\r
5058 \r
5059     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5060       RevertEvent(TRUE);\r
5061       break;\r
5062 \r
5063     case IDM_TruncateGame:\r
5064       TruncateGameEvent();\r
5065       break;\r
5066 \r
5067     case IDM_MoveNow:\r
5068       MoveNowEvent();\r
5069       break;\r
5070 \r
5071     case IDM_RetractMove:\r
5072       RetractMoveEvent();\r
5073       break;\r
5074 \r
5075     case IDM_FlipView:\r
5076       flipView = !flipView;\r
5077       DrawPosition(FALSE, NULL);\r
5078       break;\r
5079 \r
5080     case IDM_FlipClock:\r
5081       flipClock = !flipClock;\r
5082       DisplayBothClocks();\r
5083       DisplayLogos();\r
5084       break;\r
5085 \r
5086     case IDM_MuteSounds:\r
5087       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5088       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5089                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5090       break;\r
5091 \r
5092     case IDM_GeneralOptions:\r
5093       GeneralOptionsPopup(hwnd);\r
5094       DrawPosition(TRUE, NULL);\r
5095       break;\r
5096 \r
5097     case IDM_BoardOptions:\r
5098       BoardOptionsPopup(hwnd);\r
5099       break;\r
5100 \r
5101     case IDM_EnginePlayOptions:\r
5102       EnginePlayOptionsPopup(hwnd);\r
5103       break;\r
5104 \r
5105     case IDM_Engine1Options:\r
5106       EngineOptionsPopup(hwnd, &first);\r
5107       break;\r
5108 \r
5109     case IDM_Engine2Options:\r
5110       savedHwnd = hwnd;\r
5111       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5112       EngineOptionsPopup(hwnd, &second);\r
5113       break;\r
5114 \r
5115     case IDM_OptionsUCI:\r
5116       UciOptionsPopup(hwnd);\r
5117       break;\r
5118 \r
5119     case IDM_Tourney:\r
5120       TourneyPopup(hwnd);\r
5121       break;\r
5122 \r
5123     case IDM_IcsOptions:\r
5124       IcsOptionsPopup(hwnd);\r
5125       break;\r
5126 \r
5127     case IDM_Fonts:\r
5128       FontsOptionsPopup(hwnd);\r
5129       break;\r
5130 \r
5131     case IDM_Sounds:\r
5132       SoundOptionsPopup(hwnd);\r
5133       break;\r
5134 \r
5135     case IDM_CommPort:\r
5136       CommPortOptionsPopup(hwnd);\r
5137       break;\r
5138 \r
5139     case IDM_LoadOptions:\r
5140       LoadOptionsPopup(hwnd);\r
5141       break;\r
5142 \r
5143     case IDM_SaveOptions:\r
5144       SaveOptionsPopup(hwnd);\r
5145       break;\r
5146 \r
5147     case IDM_TimeControl:\r
5148       TimeControlOptionsPopup(hwnd);\r
5149       break;\r
5150 \r
5151     case IDM_SaveSettings:\r
5152       SaveSettings(settingsFileName);\r
5153       break;\r
5154 \r
5155     case IDM_SaveSettingsOnExit:\r
5156       saveSettingsOnExit = !saveSettingsOnExit;\r
5157       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5158                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5159                                          MF_CHECKED : MF_UNCHECKED));\r
5160       break;\r
5161 \r
5162     case IDM_Hint:\r
5163       HintEvent();\r
5164       break;\r
5165 \r
5166     case IDM_Book:\r
5167       BookEvent();\r
5168       break;\r
5169 \r
5170     case IDM_AboutGame:\r
5171       AboutGameEvent();\r
5172       break;\r
5173 \r
5174     case IDM_Debug:\r
5175       appData.debugMode = !appData.debugMode;\r
5176       if (appData.debugMode) {\r
5177         char dir[MSG_SIZ];\r
5178         GetCurrentDirectory(MSG_SIZ, dir);\r
5179         SetCurrentDirectory(installDir);\r
5180         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5181         SetCurrentDirectory(dir);\r
5182         setbuf(debugFP, NULL);\r
5183       } else {\r
5184         fclose(debugFP);\r
5185         debugFP = NULL;\r
5186       }\r
5187       break;\r
5188 \r
5189     case IDM_HELPCONTENTS:\r
5190       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5191           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5192           MessageBox (GetFocus(),\r
5193                     _("Unable to activate help"),\r
5194                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5195       }\r
5196       break;\r
5197 \r
5198     case IDM_HELPSEARCH:\r
5199         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5200             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5201         MessageBox (GetFocus(),\r
5202                     _("Unable to activate help"),\r
5203                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5204       }\r
5205       break;\r
5206 \r
5207     case IDM_HELPHELP:\r
5208       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5209         MessageBox (GetFocus(),\r
5210                     _("Unable to activate help"),\r
5211                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5212       }\r
5213       break;\r
5214 \r
5215     case IDM_ABOUT:\r
5216       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5217       DialogBox(hInst, \r
5218         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5219         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5220       FreeProcInstance(lpProc);\r
5221       break;\r
5222 \r
5223     case IDM_DirectCommand1:\r
5224       AskQuestionEvent(_("Direct Command"),\r
5225                        _("Send to chess program:"), "", "1");\r
5226       break;\r
5227     case IDM_DirectCommand2:\r
5228       AskQuestionEvent(_("Direct Command"),\r
5229                        _("Send to second chess program:"), "", "2");\r
5230       break;\r
5231 \r
5232     case EP_WhitePawn:\r
5233       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5234       fromX = fromY = -1;\r
5235       break;\r
5236 \r
5237     case EP_WhiteKnight:\r
5238       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5239       fromX = fromY = -1;\r
5240       break;\r
5241 \r
5242     case EP_WhiteBishop:\r
5243       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5244       fromX = fromY = -1;\r
5245       break;\r
5246 \r
5247     case EP_WhiteRook:\r
5248       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5249       fromX = fromY = -1;\r
5250       break;\r
5251 \r
5252     case EP_WhiteQueen:\r
5253       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5254       fromX = fromY = -1;\r
5255       break;\r
5256 \r
5257     case EP_WhiteFerz:\r
5258       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5259       fromX = fromY = -1;\r
5260       break;\r
5261 \r
5262     case EP_WhiteWazir:\r
5263       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5264       fromX = fromY = -1;\r
5265       break;\r
5266 \r
5267     case EP_WhiteAlfil:\r
5268       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5269       fromX = fromY = -1;\r
5270       break;\r
5271 \r
5272     case EP_WhiteCannon:\r
5273       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5274       fromX = fromY = -1;\r
5275       break;\r
5276 \r
5277     case EP_WhiteCardinal:\r
5278       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5279       fromX = fromY = -1;\r
5280       break;\r
5281 \r
5282     case EP_WhiteMarshall:\r
5283       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5284       fromX = fromY = -1;\r
5285       break;\r
5286 \r
5287     case EP_WhiteKing:\r
5288       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5289       fromX = fromY = -1;\r
5290       break;\r
5291 \r
5292     case EP_BlackPawn:\r
5293       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5294       fromX = fromY = -1;\r
5295       break;\r
5296 \r
5297     case EP_BlackKnight:\r
5298       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5299       fromX = fromY = -1;\r
5300       break;\r
5301 \r
5302     case EP_BlackBishop:\r
5303       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5304       fromX = fromY = -1;\r
5305       break;\r
5306 \r
5307     case EP_BlackRook:\r
5308       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5309       fromX = fromY = -1;\r
5310       break;\r
5311 \r
5312     case EP_BlackQueen:\r
5313       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5314       fromX = fromY = -1;\r
5315       break;\r
5316 \r
5317     case EP_BlackFerz:\r
5318       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5319       fromX = fromY = -1;\r
5320       break;\r
5321 \r
5322     case EP_BlackWazir:\r
5323       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5324       fromX = fromY = -1;\r
5325       break;\r
5326 \r
5327     case EP_BlackAlfil:\r
5328       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5329       fromX = fromY = -1;\r
5330       break;\r
5331 \r
5332     case EP_BlackCannon:\r
5333       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5334       fromX = fromY = -1;\r
5335       break;\r
5336 \r
5337     case EP_BlackCardinal:\r
5338       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5339       fromX = fromY = -1;\r
5340       break;\r
5341 \r
5342     case EP_BlackMarshall:\r
5343       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5344       fromX = fromY = -1;\r
5345       break;\r
5346 \r
5347     case EP_BlackKing:\r
5348       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5349       fromX = fromY = -1;\r
5350       break;\r
5351 \r
5352     case EP_EmptySquare:\r
5353       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5354       fromX = fromY = -1;\r
5355       break;\r
5356 \r
5357     case EP_ClearBoard:\r
5358       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5359       fromX = fromY = -1;\r
5360       break;\r
5361 \r
5362     case EP_White:\r
5363       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5364       fromX = fromY = -1;\r
5365       break;\r
5366 \r
5367     case EP_Black:\r
5368       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5369       fromX = fromY = -1;\r
5370       break;\r
5371 \r
5372     case EP_Promote:\r
5373       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5374       fromX = fromY = -1;\r
5375       break;\r
5376 \r
5377     case EP_Demote:\r
5378       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5379       fromX = fromY = -1;\r
5380       break;\r
5381 \r
5382     case DP_Pawn:\r
5383       DropMenuEvent(WhitePawn, fromX, fromY);\r
5384       fromX = fromY = -1;\r
5385       break;\r
5386 \r
5387     case DP_Knight:\r
5388       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5389       fromX = fromY = -1;\r
5390       break;\r
5391 \r
5392     case DP_Bishop:\r
5393       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5394       fromX = fromY = -1;\r
5395       break;\r
5396 \r
5397     case DP_Rook:\r
5398       DropMenuEvent(WhiteRook, fromX, fromY);\r
5399       fromX = fromY = -1;\r
5400       break;\r
5401 \r
5402     case DP_Queen:\r
5403       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5404       fromX = fromY = -1;\r
5405       break;\r
5406 \r
5407     case IDM_English:\r
5408       barbaric = 0; appData.language = "";\r
5409       TranslateMenus(0);\r
5410       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5411       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5412       lastChecked = wmId;\r
5413       break;\r
5414 \r
5415     default:\r
5416       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5417           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5418           TranslateMenus(0);\r
5419           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5420           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5421           lastChecked = wmId;\r
5422           break;\r
5423       }\r
5424       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5425     }\r
5426     break;\r
5427 \r
5428   case WM_TIMER:\r
5429     switch (wParam) {\r
5430     case CLOCK_TIMER_ID:\r
5431       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5432       clockTimerEvent = 0;\r
5433       DecrementClocks(); /* call into back end */\r
5434       break;\r
5435     case LOAD_GAME_TIMER_ID:\r
5436       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5437       loadGameTimerEvent = 0;\r
5438       AutoPlayGameLoop(); /* call into back end */\r
5439       break;\r
5440     case ANALYSIS_TIMER_ID:\r
5441       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5442                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5443         AnalysisPeriodicEvent(0);\r
5444       } else {\r
5445         KillTimer(hwnd, analysisTimerEvent);\r
5446         analysisTimerEvent = 0;\r
5447       }\r
5448       break;\r
5449     case DELAYED_TIMER_ID:\r
5450       KillTimer(hwnd, delayedTimerEvent);\r
5451       delayedTimerEvent = 0;\r
5452       delayedTimerCallback();\r
5453       break;\r
5454     }\r
5455     break;\r
5456 \r
5457   case WM_USER_Input:\r
5458     InputEvent(hwnd, message, wParam, lParam);\r
5459     break;\r
5460 \r
5461   /* [AS] Also move "attached" child windows */\r
5462   case WM_WINDOWPOSCHANGING:\r
5463 \r
5464     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5465         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5466 \r
5467         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5468             /* Window is moving */\r
5469             RECT rcMain;\r
5470 \r
5471 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5472             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5473             rcMain.right  = wpMain.x + wpMain.width;\r
5474             rcMain.top    = wpMain.y;\r
5475             rcMain.bottom = wpMain.y + wpMain.height;\r
5476             \r
5477             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5478             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5479             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5480             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5481             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5482             wpMain.x = lpwp->x;\r
5483             wpMain.y = lpwp->y;\r
5484         }\r
5485     }\r
5486     break;\r
5487 \r
5488   /* [AS] Snapping */\r
5489   case WM_ENTERSIZEMOVE:\r
5490     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5491     if (hwnd == hwndMain) {\r
5492       doingSizing = TRUE;\r
5493       lastSizing = 0;\r
5494     }\r
5495     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5496     break;\r
5497 \r
5498   case WM_SIZING:\r
5499     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5500     if (hwnd == hwndMain) {\r
5501       lastSizing = wParam;\r
5502     }\r
5503     break;\r
5504 \r
5505   case WM_MOVING:\r
5506     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5507       return OnMoving( &sd, hwnd, wParam, lParam );\r
5508 \r
5509   case WM_EXITSIZEMOVE:\r
5510     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5511     if (hwnd == hwndMain) {\r
5512       RECT client;\r
5513       doingSizing = FALSE;\r
5514       InvalidateRect(hwnd, &boardRect, FALSE);\r
5515       GetClientRect(hwnd, &client);\r
5516       ResizeBoard(client.right, client.bottom, lastSizing);\r
5517       lastSizing = 0;\r
5518       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5519     }\r
5520     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5521     break;\r
5522 \r
5523   case WM_DESTROY: /* message: window being destroyed */\r
5524     PostQuitMessage(0);\r
5525     break;\r
5526 \r
5527   case WM_CLOSE:\r
5528     if (hwnd == hwndMain) {\r
5529       ExitEvent(0);\r
5530     }\r
5531     break;\r
5532 \r
5533   default:      /* Passes it on if unprocessed */\r
5534     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5535   }\r
5536   return 0;\r
5537 }\r
5538 \r
5539 /*---------------------------------------------------------------------------*\\r
5540  *\r
5541  * Misc utility routines\r
5542  *\r
5543 \*---------------------------------------------------------------------------*/\r
5544 \r
5545 /*\r
5546  * Decent random number generator, at least not as bad as Windows\r
5547  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5548  */\r
5549 unsigned int randstate;\r
5550 \r
5551 int\r
5552 myrandom(void)\r
5553 {\r
5554   randstate = randstate * 1664525 + 1013904223;\r
5555   return (int) randstate & 0x7fffffff;\r
5556 }\r
5557 \r
5558 void\r
5559 mysrandom(unsigned int seed)\r
5560 {\r
5561   randstate = seed;\r
5562 }\r
5563 \r
5564 \r
5565 /* \r
5566  * returns TRUE if user selects a different color, FALSE otherwise \r
5567  */\r
5568 \r
5569 BOOL\r
5570 ChangeColor(HWND hwnd, COLORREF *which)\r
5571 {\r
5572   static BOOL firstTime = TRUE;\r
5573   static DWORD customColors[16];\r
5574   CHOOSECOLOR cc;\r
5575   COLORREF newcolor;\r
5576   int i;\r
5577   ColorClass ccl;\r
5578 \r
5579   if (firstTime) {\r
5580     /* Make initial colors in use available as custom colors */\r
5581     /* Should we put the compiled-in defaults here instead? */\r
5582     i = 0;\r
5583     customColors[i++] = lightSquareColor & 0xffffff;\r
5584     customColors[i++] = darkSquareColor & 0xffffff;\r
5585     customColors[i++] = whitePieceColor & 0xffffff;\r
5586     customColors[i++] = blackPieceColor & 0xffffff;\r
5587     customColors[i++] = highlightSquareColor & 0xffffff;\r
5588     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5589 \r
5590     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5591       customColors[i++] = textAttribs[ccl].color;\r
5592     }\r
5593     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5594     firstTime = FALSE;\r
5595   }\r
5596 \r
5597   cc.lStructSize = sizeof(cc);\r
5598   cc.hwndOwner = hwnd;\r
5599   cc.hInstance = NULL;\r
5600   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5601   cc.lpCustColors = (LPDWORD) customColors;\r
5602   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5603 \r
5604   if (!ChooseColor(&cc)) return FALSE;\r
5605 \r
5606   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5607   if (newcolor == *which) return FALSE;\r
5608   *which = newcolor;\r
5609   return TRUE;\r
5610 \r
5611   /*\r
5612   InitDrawingColors();\r
5613   InvalidateRect(hwnd, &boardRect, FALSE);\r
5614   */\r
5615 }\r
5616 \r
5617 BOOLEAN\r
5618 MyLoadSound(MySound *ms)\r
5619 {\r
5620   BOOL ok = FALSE;\r
5621   struct stat st;\r
5622   FILE *f;\r
5623 \r
5624   if (ms->data && ms->flag) free(ms->data);\r
5625   ms->data = NULL;\r
5626 \r
5627   switch (ms->name[0]) {\r
5628   case NULLCHAR:\r
5629     /* Silence */\r
5630     ok = TRUE;\r
5631     break;\r
5632   case '$':\r
5633     /* System sound from Control Panel.  Don't preload here. */\r
5634     ok = TRUE;\r
5635     break;\r
5636   case '!':\r
5637     if (ms->name[1] == NULLCHAR) {\r
5638       /* "!" alone = silence */\r
5639       ok = TRUE;\r
5640     } else {\r
5641       /* Builtin wave resource.  Error if not found. */\r
5642       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5643       if (h == NULL) break;\r
5644       ms->data = (void *)LoadResource(hInst, h);\r
5645       ms->flag = 0; // not maloced, so cannot be freed!\r
5646       if (h == NULL) break;\r
5647       ok = TRUE;\r
5648     }\r
5649     break;\r
5650   default:\r
5651     /* .wav file.  Error if not found. */\r
5652     f = fopen(ms->name, "rb");\r
5653     if (f == NULL) break;\r
5654     if (fstat(fileno(f), &st) < 0) break;\r
5655     ms->data = malloc(st.st_size);\r
5656     ms->flag = 1;\r
5657     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5658     fclose(f);\r
5659     ok = TRUE;\r
5660     break;\r
5661   }\r
5662   if (!ok) {\r
5663     char buf[MSG_SIZ];\r
5664       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5665     DisplayError(buf, GetLastError());\r
5666   }\r
5667   return ok;\r
5668 }\r
5669 \r
5670 BOOLEAN\r
5671 MyPlaySound(MySound *ms)\r
5672 {\r
5673   BOOLEAN ok = FALSE;\r
5674 \r
5675   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5676   switch (ms->name[0]) {\r
5677   case NULLCHAR:\r
5678         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5679     /* Silence */\r
5680     ok = TRUE;\r
5681     break;\r
5682   case '$':\r
5683     /* System sound from Control Panel (deprecated feature).\r
5684        "$" alone or an unset sound name gets default beep (still in use). */\r
5685     if (ms->name[1]) {\r
5686       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5687     }\r
5688     if (!ok) ok = MessageBeep(MB_OK);\r
5689     break; \r
5690   case '!':\r
5691     /* Builtin wave resource, or "!" alone for silence */\r
5692     if (ms->name[1]) {\r
5693       if (ms->data == NULL) return FALSE;\r
5694       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5695     } else {\r
5696       ok = TRUE;\r
5697     }\r
5698     break;\r
5699   default:\r
5700     /* .wav file.  Error if not found. */\r
5701     if (ms->data == NULL) return FALSE;\r
5702     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5703     break;\r
5704   }\r
5705   /* Don't print an error: this can happen innocently if the sound driver\r
5706      is busy; for instance, if another instance of WinBoard is playing\r
5707      a sound at about the same time. */\r
5708   return ok;\r
5709 }\r
5710 \r
5711 \r
5712 LRESULT CALLBACK\r
5713 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5714 {\r
5715   BOOL ok;\r
5716   OPENFILENAME *ofn;\r
5717   static UINT *number; /* gross that this is static */\r
5718 \r
5719   switch (message) {\r
5720   case WM_INITDIALOG: /* message: initialize dialog box */\r
5721     /* Center the dialog over the application window */\r
5722     ofn = (OPENFILENAME *) lParam;\r
5723     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5724       number = (UINT *) ofn->lCustData;\r
5725       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5726     } else {\r
5727       number = NULL;\r
5728     }\r
5729     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5730     Translate(hDlg, 1536);\r
5731     return FALSE;  /* Allow for further processing */\r
5732 \r
5733   case WM_COMMAND:\r
5734     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5735       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5736     }\r
5737     return FALSE;  /* Allow for further processing */\r
5738   }\r
5739   return FALSE;\r
5740 }\r
5741 \r
5742 UINT APIENTRY\r
5743 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5744 {\r
5745   static UINT *number;\r
5746   OPENFILENAME *ofname;\r
5747   OFNOTIFY *ofnot;\r
5748   switch (uiMsg) {\r
5749   case WM_INITDIALOG:\r
5750     Translate(hdlg, DLG_IndexNumber);\r
5751     ofname = (OPENFILENAME *)lParam;\r
5752     number = (UINT *)(ofname->lCustData);\r
5753     break;\r
5754   case WM_NOTIFY:\r
5755     ofnot = (OFNOTIFY *)lParam;\r
5756     if (ofnot->hdr.code == CDN_FILEOK) {\r
5757       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5758     }\r
5759     break;\r
5760   }\r
5761   return 0;\r
5762 }\r
5763 \r
5764 \r
5765 FILE *\r
5766 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5767                char *nameFilt, char *dlgTitle, UINT *number,\r
5768                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5769 {\r
5770   OPENFILENAME openFileName;\r
5771   char buf1[MSG_SIZ];\r
5772   FILE *f;\r
5773 \r
5774   if (fileName == NULL) fileName = buf1;\r
5775   if (defName == NULL) {\r
5776     safeStrCpy(fileName, "*.", 3 );\r
5777     strcat(fileName, defExt);\r
5778   } else {\r
5779     safeStrCpy(fileName, defName, MSG_SIZ );\r
5780   }\r
5781     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5782   if (number) *number = 0;\r
5783 \r
5784   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5785   openFileName.hwndOwner         = hwnd;\r
5786   openFileName.hInstance         = (HANDLE) hInst;\r
5787   openFileName.lpstrFilter       = nameFilt;\r
5788   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5789   openFileName.nMaxCustFilter    = 0L;\r
5790   openFileName.nFilterIndex      = 1L;\r
5791   openFileName.lpstrFile         = fileName;\r
5792   openFileName.nMaxFile          = MSG_SIZ;\r
5793   openFileName.lpstrFileTitle    = fileTitle;\r
5794   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5795   openFileName.lpstrInitialDir   = NULL;\r
5796   openFileName.lpstrTitle        = dlgTitle;\r
5797   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5798     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5799     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5800     | (oldDialog ? 0 : OFN_EXPLORER);\r
5801   openFileName.nFileOffset       = 0;\r
5802   openFileName.nFileExtension    = 0;\r
5803   openFileName.lpstrDefExt       = defExt;\r
5804   openFileName.lCustData         = (LONG) number;\r
5805   openFileName.lpfnHook          = oldDialog ?\r
5806     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5807   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5808 \r
5809   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5810                         GetOpenFileName(&openFileName)) {\r
5811     /* open the file */\r
5812     f = fopen(openFileName.lpstrFile, write);\r
5813     if (f == NULL) {\r
5814       MessageBox(hwnd, _("File open failed"), NULL,\r
5815                  MB_OK|MB_ICONEXCLAMATION);\r
5816       return NULL;\r
5817     }\r
5818   } else {\r
5819     int err = CommDlgExtendedError();\r
5820     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5821     return FALSE;\r
5822   }\r
5823   return f;\r
5824 }\r
5825 \r
5826 \r
5827 \r
5828 VOID APIENTRY\r
5829 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5830 {\r
5831   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5832 \r
5833   /*\r
5834    * Get the first pop-up menu in the menu template. This is the\r
5835    * menu that TrackPopupMenu displays.\r
5836    */\r
5837   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5838   TranslateOneMenu(10, hmenuTrackPopup);\r
5839 \r
5840   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5841 \r
5842   /*\r
5843    * TrackPopup uses screen coordinates, so convert the\r
5844    * coordinates of the mouse click to screen coordinates.\r
5845    */\r
5846   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5847 \r
5848   /* Draw and track the floating pop-up menu. */\r
5849   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5850                  pt.x, pt.y, 0, hwnd, NULL);\r
5851 \r
5852   /* Destroy the menu.*/\r
5853   DestroyMenu(hmenu);\r
5854 }\r
5855    \r
5856 typedef struct {\r
5857   HWND hDlg, hText;\r
5858   int sizeX, sizeY, newSizeX, newSizeY;\r
5859   HDWP hdwp;\r
5860 } ResizeEditPlusButtonsClosure;\r
5861 \r
5862 BOOL CALLBACK\r
5863 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5864 {\r
5865   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5866   RECT rect;\r
5867   POINT pt;\r
5868 \r
5869   if (hChild == cl->hText) return TRUE;\r
5870   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5871   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5872   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5873   ScreenToClient(cl->hDlg, &pt);\r
5874   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5875     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5876   return TRUE;\r
5877 }\r
5878 \r
5879 /* Resize a dialog that has a (rich) edit field filling most of\r
5880    the top, with a row of buttons below */\r
5881 VOID\r
5882 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5883 {\r
5884   RECT rectText;\r
5885   int newTextHeight, newTextWidth;\r
5886   ResizeEditPlusButtonsClosure cl;\r
5887   \r
5888   /*if (IsIconic(hDlg)) return;*/\r
5889   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5890   \r
5891   cl.hdwp = BeginDeferWindowPos(8);\r
5892 \r
5893   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5894   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5895   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5896   if (newTextHeight < 0) {\r
5897     newSizeY += -newTextHeight;\r
5898     newTextHeight = 0;\r
5899   }\r
5900   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5901     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5902 \r
5903   cl.hDlg = hDlg;\r
5904   cl.hText = hText;\r
5905   cl.sizeX = sizeX;\r
5906   cl.sizeY = sizeY;\r
5907   cl.newSizeX = newSizeX;\r
5908   cl.newSizeY = newSizeY;\r
5909   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5910 \r
5911   EndDeferWindowPos(cl.hdwp);\r
5912 }\r
5913 \r
5914 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5915 {\r
5916     RECT    rChild, rParent;\r
5917     int     wChild, hChild, wParent, hParent;\r
5918     int     wScreen, hScreen, xNew, yNew;\r
5919     HDC     hdc;\r
5920 \r
5921     /* Get the Height and Width of the child window */\r
5922     GetWindowRect (hwndChild, &rChild);\r
5923     wChild = rChild.right - rChild.left;\r
5924     hChild = rChild.bottom - rChild.top;\r
5925 \r
5926     /* Get the Height and Width of the parent window */\r
5927     GetWindowRect (hwndParent, &rParent);\r
5928     wParent = rParent.right - rParent.left;\r
5929     hParent = rParent.bottom - rParent.top;\r
5930 \r
5931     /* Get the display limits */\r
5932     hdc = GetDC (hwndChild);\r
5933     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5934     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5935     ReleaseDC(hwndChild, hdc);\r
5936 \r
5937     /* Calculate new X position, then adjust for screen */\r
5938     xNew = rParent.left + ((wParent - wChild) /2);\r
5939     if (xNew < 0) {\r
5940         xNew = 0;\r
5941     } else if ((xNew+wChild) > wScreen) {\r
5942         xNew = wScreen - wChild;\r
5943     }\r
5944 \r
5945     /* Calculate new Y position, then adjust for screen */\r
5946     if( mode == 0 ) {\r
5947         yNew = rParent.top  + ((hParent - hChild) /2);\r
5948     }\r
5949     else {\r
5950         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5951     }\r
5952 \r
5953     if (yNew < 0) {\r
5954         yNew = 0;\r
5955     } else if ((yNew+hChild) > hScreen) {\r
5956         yNew = hScreen - hChild;\r
5957     }\r
5958 \r
5959     /* Set it, and return */\r
5960     return SetWindowPos (hwndChild, NULL,\r
5961                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5962 }\r
5963 \r
5964 /* Center one window over another */\r
5965 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5966 {\r
5967     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5968 }\r
5969 \r
5970 /*---------------------------------------------------------------------------*\\r
5971  *\r
5972  * Startup Dialog functions\r
5973  *\r
5974 \*---------------------------------------------------------------------------*/\r
5975 void\r
5976 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5977 {\r
5978   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5979 \r
5980   while (*cd != NULL) {\r
5981     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5982     cd++;\r
5983   }\r
5984 }\r
5985 \r
5986 void\r
5987 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5988 {\r
5989   char buf1[MAX_ARG_LEN];\r
5990   int len;\r
5991 \r
5992   if (str[0] == '@') {\r
5993     FILE* f = fopen(str + 1, "r");\r
5994     if (f == NULL) {\r
5995       DisplayFatalError(str + 1, errno, 2);\r
5996       return;\r
5997     }\r
5998     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5999     fclose(f);\r
6000     buf1[len] = NULLCHAR;\r
6001     str = buf1;\r
6002   }\r
6003 \r
6004   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6005 \r
6006   for (;;) {\r
6007     char buf[MSG_SIZ];\r
6008     char *end = strchr(str, '\n');\r
6009     if (end == NULL) return;\r
6010     memcpy(buf, str, end - str);\r
6011     buf[end - str] = NULLCHAR;\r
6012     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6013     str = end + 1;\r
6014   }\r
6015 }\r
6016 \r
6017 void\r
6018 SetStartupDialogEnables(HWND hDlg)\r
6019 {\r
6020   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6021     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6022     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6023   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6024     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6025   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6026     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6027   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6028     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6029   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6030     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6031     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6032     IsDlgButtonChecked(hDlg, OPT_View));\r
6033 }\r
6034 \r
6035 char *\r
6036 QuoteForFilename(char *filename)\r
6037 {\r
6038   int dquote, space;\r
6039   dquote = strchr(filename, '"') != NULL;\r
6040   space = strchr(filename, ' ') != NULL;\r
6041   if (dquote || space) {\r
6042     if (dquote) {\r
6043       return "'";\r
6044     } else {\r
6045       return "\"";\r
6046     }\r
6047   } else {\r
6048     return "";\r
6049   }\r
6050 }\r
6051 \r
6052 VOID\r
6053 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6054 {\r
6055   char buf[MSG_SIZ];\r
6056   char *q;\r
6057 \r
6058   InitComboStringsFromOption(hwndCombo, nthnames);\r
6059   q = QuoteForFilename(nthcp);\r
6060     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6061   if (*nthdir != NULLCHAR) {\r
6062     q = QuoteForFilename(nthdir);\r
6063       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6064   }\r
6065   if (*nthcp == NULLCHAR) {\r
6066     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6067   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6068     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6069     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6070   }\r
6071 }\r
6072 \r
6073 LRESULT CALLBACK\r
6074 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6075 {\r
6076   char buf[MSG_SIZ];\r
6077   HANDLE hwndCombo;\r
6078   char *p;\r
6079 \r
6080   switch (message) {\r
6081   case WM_INITDIALOG:\r
6082     /* Center the dialog */\r
6083     CenterWindow (hDlg, GetDesktopWindow());\r
6084     Translate(hDlg, DLG_Startup);\r
6085     /* Initialize the dialog items */\r
6086     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6087                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6088                   firstChessProgramNames);\r
6089     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6090                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6091                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6092     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6093     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6094       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6095     if (*appData.icsHelper != NULLCHAR) {\r
6096       char *q = QuoteForFilename(appData.icsHelper);\r
6097       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6098     }\r
6099     if (*appData.icsHost == NULLCHAR) {\r
6100       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6101       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6102     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6103       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6104       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6105     }\r
6106 \r
6107     if (appData.icsActive) {\r
6108       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6109     }\r
6110     else if (appData.noChessProgram) {\r
6111       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6112     }\r
6113     else {\r
6114       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6115     }\r
6116 \r
6117     SetStartupDialogEnables(hDlg);\r
6118     return TRUE;\r
6119 \r
6120   case WM_COMMAND:\r
6121     switch (LOWORD(wParam)) {\r
6122     case IDOK:\r
6123       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6124         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6125         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6126         p = buf;\r
6127         ParseArgs(StringGet, &p);\r
6128         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6129         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6130         p = buf;
6131         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6132         ParseArgs(StringGet, &p);\r
6133         SwapEngines(singleList); // ... and then make it 'second'\r
6134         appData.noChessProgram = FALSE;\r
6135         appData.icsActive = FALSE;\r
6136       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6137         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6138         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6139         p = buf;\r
6140         ParseArgs(StringGet, &p);\r
6141         if (appData.zippyPlay) {\r
6142           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6143           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6144           p = buf;\r
6145           ParseArgs(StringGet, &p);\r
6146         }\r
6147       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6148         appData.noChessProgram = TRUE;\r
6149         appData.icsActive = FALSE;\r
6150       } else {\r
6151         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6152                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6153         return TRUE;\r
6154       }\r
6155       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6156         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6157         p = buf;\r
6158         ParseArgs(StringGet, &p);\r
6159       }\r
6160       EndDialog(hDlg, TRUE);\r
6161       return TRUE;\r
6162 \r
6163     case IDCANCEL:\r
6164       ExitEvent(0);\r
6165       return TRUE;\r
6166 \r
6167     case IDM_HELPCONTENTS:\r
6168       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6169         MessageBox (GetFocus(),\r
6170                     _("Unable to activate help"),\r
6171                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6172       }\r
6173       break;\r
6174 \r
6175     default:\r
6176       SetStartupDialogEnables(hDlg);\r
6177       break;\r
6178     }\r
6179     break;\r
6180   }\r
6181   return FALSE;\r
6182 }\r
6183 \r
6184 /*---------------------------------------------------------------------------*\\r
6185  *\r
6186  * About box dialog functions\r
6187  *\r
6188 \*---------------------------------------------------------------------------*/\r
6189 \r
6190 /* Process messages for "About" dialog box */\r
6191 LRESULT CALLBACK\r
6192 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6193 {\r
6194   switch (message) {\r
6195   case WM_INITDIALOG: /* message: initialize dialog box */\r
6196     /* Center the dialog over the application window */\r
6197     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6198     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6199     Translate(hDlg, ABOUTBOX);\r
6200     JAWS_COPYRIGHT\r
6201     return (TRUE);\r
6202 \r
6203   case WM_COMMAND: /* message: received a command */\r
6204     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6205         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6206       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6207       return (TRUE);\r
6208     }\r
6209     break;\r
6210   }\r
6211   return (FALSE);\r
6212 }\r
6213 \r
6214 /*---------------------------------------------------------------------------*\\r
6215  *\r
6216  * Comment Dialog functions\r
6217  *\r
6218 \*---------------------------------------------------------------------------*/\r
6219 \r
6220 LRESULT CALLBACK\r
6221 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6222 {\r
6223   static HANDLE hwndText = NULL;\r
6224   int len, newSizeX, newSizeY, flags;\r
6225   static int sizeX, sizeY;\r
6226   char *str;\r
6227   RECT rect;\r
6228   MINMAXINFO *mmi;\r
6229 \r
6230   switch (message) {\r
6231   case WM_INITDIALOG: /* message: initialize dialog box */\r
6232     /* Initialize the dialog items */\r
6233     Translate(hDlg, DLG_EditComment);\r
6234     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6235     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6236     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6237     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6238     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6239     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6240     SetWindowText(hDlg, commentTitle);\r
6241     if (editComment) {\r
6242       SetFocus(hwndText);\r
6243     } else {\r
6244       SetFocus(GetDlgItem(hDlg, IDOK));\r
6245     }\r
6246     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6247                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6248                 MAKELPARAM(FALSE, 0));\r
6249     /* Size and position the dialog */\r
6250     if (!commentDialog) {\r
6251       commentDialog = hDlg;\r
6252       flags = SWP_NOZORDER;\r
6253       GetClientRect(hDlg, &rect);\r
6254       sizeX = rect.right;\r
6255       sizeY = rect.bottom;\r
6256       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6257           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6258         WINDOWPLACEMENT wp;\r
6259         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6260         wp.length = sizeof(WINDOWPLACEMENT);\r
6261         wp.flags = 0;\r
6262         wp.showCmd = SW_SHOW;\r
6263         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6264         wp.rcNormalPosition.left = wpComment.x;\r
6265         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6266         wp.rcNormalPosition.top = wpComment.y;\r
6267         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6268         SetWindowPlacement(hDlg, &wp);\r
6269 \r
6270         GetClientRect(hDlg, &rect);\r
6271         newSizeX = rect.right;\r
6272         newSizeY = rect.bottom;\r
6273         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6274                               newSizeX, newSizeY);\r
6275         sizeX = newSizeX;\r
6276         sizeY = newSizeY;\r
6277       }\r
6278     }\r
6279     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6280     return FALSE;\r
6281 \r
6282   case WM_COMMAND: /* message: received a command */\r
6283     switch (LOWORD(wParam)) {\r
6284     case IDOK:\r
6285       if (editComment) {\r
6286         char *p, *q;\r
6287         /* Read changed options from the dialog box */\r
6288         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6289         len = GetWindowTextLength(hwndText);\r
6290         str = (char *) malloc(len + 1);\r
6291         GetWindowText(hwndText, str, len + 1);\r
6292         p = q = str;\r
6293         while (*q) {\r
6294           if (*q == '\r')\r
6295             q++;\r
6296           else\r
6297             *p++ = *q++;\r
6298         }\r
6299         *p = NULLCHAR;\r
6300         ReplaceComment(commentIndex, str);\r
6301         free(str);\r
6302       }\r
6303       CommentPopDown();\r
6304       return TRUE;\r
6305 \r
6306     case IDCANCEL:\r
6307     case OPT_CancelComment:\r
6308       CommentPopDown();\r
6309       return TRUE;\r
6310 \r
6311     case OPT_ClearComment:\r
6312       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6313       break;\r
6314 \r
6315     case OPT_EditComment:\r
6316       EditCommentEvent();\r
6317       return TRUE;\r
6318 \r
6319     default:\r
6320       break;\r
6321     }\r
6322     break;\r
6323 \r
6324   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6325         if( wParam == OPT_CommentText ) {\r
6326             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6327 \r
6328             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6329                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6330                 POINTL pt;\r
6331                 LRESULT index;\r
6332 \r
6333                 pt.x = LOWORD( lpMF->lParam );\r
6334                 pt.y = HIWORD( lpMF->lParam );\r
6335 \r
6336                 if(lpMF->msg == WM_CHAR) {\r
6337                         CHARRANGE sel;\r
6338                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6339                         index = sel.cpMin;\r
6340                 } else\r
6341                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6342 \r
6343                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6344                 len = GetWindowTextLength(hwndText);\r
6345                 str = (char *) malloc(len + 1);\r
6346                 GetWindowText(hwndText, str, len + 1);\r
6347                 ReplaceComment(commentIndex, str);\r
6348                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6349                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6350                 free(str);\r
6351 \r
6352                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6353                 lpMF->msg = WM_USER;\r
6354 \r
6355                 return TRUE;\r
6356             }\r
6357         }\r
6358         break;\r
6359 \r
6360   case WM_SIZE:\r
6361     newSizeX = LOWORD(lParam);\r
6362     newSizeY = HIWORD(lParam);\r
6363     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6364     sizeX = newSizeX;\r
6365     sizeY = newSizeY;\r
6366     break;\r
6367 \r
6368   case WM_GETMINMAXINFO:\r
6369     /* Prevent resizing window too small */\r
6370     mmi = (MINMAXINFO *) lParam;\r
6371     mmi->ptMinTrackSize.x = 100;\r
6372     mmi->ptMinTrackSize.y = 100;\r
6373     break;\r
6374   }\r
6375   return FALSE;\r
6376 }\r
6377 \r
6378 VOID\r
6379 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6380 {\r
6381   FARPROC lpProc;\r
6382   char *p, *q;\r
6383 \r
6384   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6385 \r
6386   if (str == NULL) str = "";\r
6387   p = (char *) malloc(2 * strlen(str) + 2);\r
6388   q = p;\r
6389   while (*str) {\r
6390     if (*str == '\n') *q++ = '\r';\r
6391     *q++ = *str++;\r
6392   }\r
6393   *q = NULLCHAR;\r
6394   if (commentText != NULL) free(commentText);\r
6395 \r
6396   commentIndex = index;\r
6397   commentTitle = title;\r
6398   commentText = p;\r
6399   editComment = edit;\r
6400 \r
6401   if (commentDialog) {\r
6402     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6403     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6404   } else {\r
6405     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6406     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6407                  hwndMain, (DLGPROC)lpProc);\r
6408     FreeProcInstance(lpProc);\r
6409   }\r
6410   commentUp = TRUE;\r
6411 }\r
6412 \r
6413 \r
6414 /*---------------------------------------------------------------------------*\\r
6415  *\r
6416  * Type-in move dialog functions\r
6417  * \r
6418 \*---------------------------------------------------------------------------*/\r
6419 \r
6420 LRESULT CALLBACK\r
6421 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6422 {\r
6423   char move[MSG_SIZ];\r
6424   HWND hInput;\r
6425 \r
6426   switch (message) {\r
6427   case WM_INITDIALOG:\r
6428     move[0] = (char) lParam;\r
6429     move[1] = NULLCHAR;\r
6430     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6431     Translate(hDlg, DLG_TypeInMove);\r
6432     hInput = GetDlgItem(hDlg, OPT_Move);\r
6433     SetWindowText(hInput, move);\r
6434     SetFocus(hInput);\r
6435     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6436     return FALSE;\r
6437 \r
6438   case WM_COMMAND:\r
6439     switch (LOWORD(wParam)) {\r
6440     case IDOK:
6441 \r
6442       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6443       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6444       TypeInDoneEvent(move);\r
6445       EndDialog(hDlg, TRUE);\r
6446       return TRUE;\r
6447     case IDCANCEL:\r
6448       EndDialog(hDlg, FALSE);\r
6449       return TRUE;\r
6450     default:\r
6451       break;\r
6452     }\r
6453     break;\r
6454   }\r
6455   return FALSE;\r
6456 }\r
6457 \r
6458 VOID\r
6459 PopUpMoveDialog(char firstchar)\r
6460 {\r
6461     FARPROC lpProc;\r
6462 \r
6463       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6464       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6465         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6466       FreeProcInstance(lpProc);\r
6467 }\r
6468 \r
6469 /*---------------------------------------------------------------------------*\\r
6470  *\r
6471  * Type-in name dialog functions\r
6472  * \r
6473 \*---------------------------------------------------------------------------*/\r
6474 \r
6475 LRESULT CALLBACK\r
6476 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6477 {\r
6478   char move[MSG_SIZ];\r
6479   HWND hInput;\r
6480 \r
6481   switch (message) {\r
6482   case WM_INITDIALOG:\r
6483     move[0] = (char) lParam;\r
6484     move[1] = NULLCHAR;\r
6485     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6486     Translate(hDlg, DLG_TypeInName);\r
6487     hInput = GetDlgItem(hDlg, OPT_Name);\r
6488     SetWindowText(hInput, move);\r
6489     SetFocus(hInput);\r
6490     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6491     return FALSE;\r
6492 \r
6493   case WM_COMMAND:\r
6494     switch (LOWORD(wParam)) {\r
6495     case IDOK:\r
6496       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6497       appData.userName = strdup(move);\r
6498       SetUserLogo();\r
6499       SetGameInfo();\r
6500       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6501         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6502         DisplayTitle(move);\r
6503       }\r
6504 \r
6505 \r
6506       EndDialog(hDlg, TRUE);\r
6507       return TRUE;\r
6508     case IDCANCEL:\r
6509       EndDialog(hDlg, FALSE);\r
6510       return TRUE;\r
6511     default:\r
6512       break;\r
6513     }\r
6514     break;\r
6515   }\r
6516   return FALSE;\r
6517 }\r
6518 \r
6519 VOID\r
6520 PopUpNameDialog(char firstchar)\r
6521 {\r
6522     FARPROC lpProc;\r
6523     \r
6524       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6525       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6526         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6527       FreeProcInstance(lpProc);\r
6528 }\r
6529 \r
6530 /*---------------------------------------------------------------------------*\\r
6531  *\r
6532  *  Error dialogs\r
6533  * \r
6534 \*---------------------------------------------------------------------------*/\r
6535 \r
6536 /* Nonmodal error box */\r
6537 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6538                              WPARAM wParam, LPARAM lParam);\r
6539 \r
6540 VOID\r
6541 ErrorPopUp(char *title, char *content)\r
6542 {\r
6543   FARPROC lpProc;\r
6544   char *p, *q;\r
6545   BOOLEAN modal = hwndMain == NULL;\r
6546 \r
6547   p = content;\r
6548   q = errorMessage;\r
6549   while (*p) {\r
6550     if (*p == '\n') {\r
6551       if (modal) {\r
6552         *q++ = ' ';\r
6553         p++;\r
6554       } else {\r
6555         *q++ = '\r';\r
6556         *q++ = *p++;\r
6557       }\r
6558     } else {\r
6559       *q++ = *p++;\r
6560     }\r
6561   }\r
6562   *q = NULLCHAR;\r
6563   strncpy(errorTitle, title, sizeof(errorTitle));\r
6564   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6565   \r
6566   if (modal) {\r
6567     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6568   } else {\r
6569     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6570     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6571                  hwndMain, (DLGPROC)lpProc);\r
6572     FreeProcInstance(lpProc);\r
6573   }\r
6574 }\r
6575 \r
6576 VOID\r
6577 ErrorPopDown()\r
6578 {\r
6579   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6580   if (errorDialog == NULL) return;\r
6581   DestroyWindow(errorDialog);\r
6582   errorDialog = NULL;\r
6583   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6584 }\r
6585 \r
6586 LRESULT CALLBACK\r
6587 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6588 {\r
6589   HANDLE hwndText;\r
6590   RECT rChild;\r
6591 \r
6592   switch (message) {\r
6593   case WM_INITDIALOG:\r
6594     GetWindowRect(hDlg, &rChild);\r
6595 \r
6596     /*\r
6597     SetWindowPos(hDlg, NULL, rChild.left,\r
6598       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6599       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6600     */\r
6601 \r
6602     /* \r
6603         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6604         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6605         and it doesn't work when you resize the dialog.\r
6606         For now, just give it a default position.\r
6607     */\r
6608     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6609     Translate(hDlg, DLG_Error);\r
6610 \r
6611     errorDialog = hDlg;\r
6612     SetWindowText(hDlg, errorTitle);\r
6613     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6614     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6615     return FALSE;\r
6616 \r
6617   case WM_COMMAND:\r
6618     switch (LOWORD(wParam)) {\r
6619     case IDOK:\r
6620     case IDCANCEL:\r
6621       if (errorDialog == hDlg) errorDialog = NULL;\r
6622       DestroyWindow(hDlg);\r
6623       return TRUE;\r
6624 \r
6625     default:\r
6626       break;\r
6627     }\r
6628     break;\r
6629   }\r
6630   return FALSE;\r
6631 }\r
6632 \r
6633 #ifdef GOTHIC\r
6634 HWND gothicDialog = NULL;\r
6635 \r
6636 LRESULT CALLBACK\r
6637 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6638 {\r
6639   HANDLE hwndText;\r
6640   RECT rChild;\r
6641   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6642 \r
6643   switch (message) {\r
6644   case WM_INITDIALOG:\r
6645     GetWindowRect(hDlg, &rChild);\r
6646 \r
6647     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6648                                                              SWP_NOZORDER);\r
6649 \r
6650     /* \r
6651         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6652         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6653         and it doesn't work when you resize the dialog.\r
6654         For now, just give it a default position.\r
6655     */\r
6656     gothicDialog = hDlg;\r
6657     SetWindowText(hDlg, errorTitle);\r
6658     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6659     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6660     return FALSE;\r
6661 \r
6662   case WM_COMMAND:\r
6663     switch (LOWORD(wParam)) {\r
6664     case IDOK:\r
6665     case IDCANCEL:\r
6666       if (errorDialog == hDlg) errorDialog = NULL;\r
6667       DestroyWindow(hDlg);\r
6668       return TRUE;\r
6669 \r
6670     default:\r
6671       break;\r
6672     }\r
6673     break;\r
6674   }\r
6675   return FALSE;\r
6676 }\r
6677 \r
6678 VOID\r
6679 GothicPopUp(char *title, VariantClass variant)\r
6680 {\r
6681   FARPROC lpProc;\r
6682   static char *lastTitle;\r
6683 \r
6684   strncpy(errorTitle, title, sizeof(errorTitle));\r
6685   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6686 \r
6687   if(lastTitle != title && gothicDialog != NULL) {\r
6688     DestroyWindow(gothicDialog);\r
6689     gothicDialog = NULL;\r
6690   }\r
6691   if(variant != VariantNormal && gothicDialog == NULL) {\r
6692     title = lastTitle;\r
6693     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6694     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6695                  hwndMain, (DLGPROC)lpProc);\r
6696     FreeProcInstance(lpProc);\r
6697   }\r
6698 }\r
6699 #endif\r
6700 \r
6701 /*---------------------------------------------------------------------------*\\r
6702  *\r
6703  *  Ics Interaction console functions\r
6704  *\r
6705 \*---------------------------------------------------------------------------*/\r
6706 \r
6707 #define HISTORY_SIZE 64\r
6708 static char *history[HISTORY_SIZE];\r
6709 int histIn = 0, histP = 0;\r
6710 \r
6711 VOID\r
6712 SaveInHistory(char *cmd)\r
6713 {\r
6714   if (history[histIn] != NULL) {\r
6715     free(history[histIn]);\r
6716     history[histIn] = NULL;\r
6717   }\r
6718   if (*cmd == NULLCHAR) return;\r
6719   history[histIn] = StrSave(cmd);\r
6720   histIn = (histIn + 1) % HISTORY_SIZE;\r
6721   if (history[histIn] != NULL) {\r
6722     free(history[histIn]);\r
6723     history[histIn] = NULL;\r
6724   }\r
6725   histP = histIn;\r
6726 }\r
6727 \r
6728 char *\r
6729 PrevInHistory(char *cmd)\r
6730 {\r
6731   int newhp;\r
6732   if (histP == histIn) {\r
6733     if (history[histIn] != NULL) free(history[histIn]);\r
6734     history[histIn] = StrSave(cmd);\r
6735   }\r
6736   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6737   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6738   histP = newhp;\r
6739   return history[histP];\r
6740 }\r
6741 \r
6742 char *\r
6743 NextInHistory()\r
6744 {\r
6745   if (histP == histIn) return NULL;\r
6746   histP = (histP + 1) % HISTORY_SIZE;\r
6747   return history[histP];   \r
6748 }\r
6749 \r
6750 HMENU\r
6751 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6752 {\r
6753   HMENU hmenu, h;\r
6754   int i = 0;\r
6755   hmenu = LoadMenu(hInst, "TextMenu");\r
6756   h = GetSubMenu(hmenu, 0);\r
6757   while (e->item) {\r
6758     if (strcmp(e->item, "-") == 0) {\r
6759       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6760     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6761       int flags = MF_STRING, j = 0;\r
6762       if (e->item[0] == '|') {\r
6763         flags |= MF_MENUBARBREAK;\r
6764         j++;\r
6765       }\r
6766       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6767       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6768     }\r
6769     e++;\r
6770     i++;\r
6771   } \r
6772   return hmenu;\r
6773 }\r
6774 \r
6775 WNDPROC consoleTextWindowProc;\r
6776 \r
6777 void\r
6778 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6779 {\r
6780   char buf[MSG_SIZ], name[MSG_SIZ];\r
6781   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6782   CHARRANGE sel;\r
6783 \r
6784   if (!getname) {\r
6785     SetWindowText(hInput, command);\r
6786     if (immediate) {\r
6787       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6788     } else {\r
6789       sel.cpMin = 999999;\r
6790       sel.cpMax = 999999;\r
6791       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6792       SetFocus(hInput);\r
6793     }\r
6794     return;\r
6795   }    \r
6796   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6797   if (sel.cpMin == sel.cpMax) {\r
6798     /* Expand to surrounding word */\r
6799     TEXTRANGE tr;\r
6800     do {\r
6801       tr.chrg.cpMax = sel.cpMin;\r
6802       tr.chrg.cpMin = --sel.cpMin;\r
6803       if (sel.cpMin < 0) break;\r
6804       tr.lpstrText = name;\r
6805       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6806     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6807     sel.cpMin++;\r
6808 \r
6809     do {\r
6810       tr.chrg.cpMin = sel.cpMax;\r
6811       tr.chrg.cpMax = ++sel.cpMax;\r
6812       tr.lpstrText = name;\r
6813       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6814     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6815     sel.cpMax--;\r
6816 \r
6817     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6818       MessageBeep(MB_ICONEXCLAMATION);\r
6819       return;\r
6820     }\r
6821     tr.chrg = sel;\r
6822     tr.lpstrText = name;\r
6823     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6824   } else {\r
6825     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6826       MessageBeep(MB_ICONEXCLAMATION);\r
6827       return;\r
6828     }\r
6829     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6830   }\r
6831   if (immediate) {\r
6832     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6833     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6834     SetWindowText(hInput, buf);\r
6835     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6836   } else {\r
6837     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6838       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6839     SetWindowText(hInput, buf);\r
6840     sel.cpMin = 999999;\r
6841     sel.cpMax = 999999;\r
6842     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6843     SetFocus(hInput);\r
6844   }\r
6845 }\r
6846 \r
6847 LRESULT CALLBACK \r
6848 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6849 {\r
6850   HWND hInput;\r
6851   CHARRANGE sel;\r
6852 \r
6853   switch (message) {\r
6854   case WM_KEYDOWN:\r
6855     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6856     if(wParam=='R') return 0;\r
6857     switch (wParam) {\r
6858     case VK_PRIOR:\r
6859       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6860       return 0;\r
6861     case VK_NEXT:\r
6862       sel.cpMin = 999999;\r
6863       sel.cpMax = 999999;\r
6864       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6865       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6866       return 0;\r
6867     }\r
6868     break;\r
6869   case WM_CHAR:\r
6870    if(wParam != '\022') {\r
6871     if (wParam == '\t') {\r
6872       if (GetKeyState(VK_SHIFT) < 0) {\r
6873         /* shifted */\r
6874         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6875         if (buttonDesc[0].hwnd) {\r
6876           SetFocus(buttonDesc[0].hwnd);\r
6877         } else {\r
6878           SetFocus(hwndMain);\r
6879         }\r
6880       } else {\r
6881         /* unshifted */\r
6882         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6883       }\r
6884     } else {\r
6885       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6886       JAWS_DELETE( SetFocus(hInput); )\r
6887       SendMessage(hInput, message, wParam, lParam);\r
6888     }\r
6889     return 0;\r
6890    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6891    lParam = -1;\r
6892   case WM_RBUTTONDOWN:\r
6893     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6894       /* Move selection here if it was empty */\r
6895       POINT pt;\r
6896       pt.x = LOWORD(lParam);\r
6897       pt.y = HIWORD(lParam);\r
6898       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6899       if (sel.cpMin == sel.cpMax) {\r
6900         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6901         sel.cpMax = sel.cpMin;\r
6902         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6903       }\r
6904       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6905 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6906       POINT pt;\r
6907       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6908       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6909       if (sel.cpMin == sel.cpMax) {\r
6910         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6911         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6912       }\r
6913       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6914         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6915       }\r
6916       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6917       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6918       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6919       MenuPopup(hwnd, pt, hmenu, -1);\r
6920 }\r
6921     }\r
6922     return 0;\r
6923   case WM_RBUTTONUP:\r
6924     if (GetKeyState(VK_SHIFT) & ~1) {\r
6925       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6926         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6927     }\r
6928     return 0;\r
6929   case WM_PASTE:\r
6930     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6931     SetFocus(hInput);\r
6932     return SendMessage(hInput, message, wParam, lParam);\r
6933   case WM_MBUTTONDOWN:\r
6934     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6935   case WM_COMMAND:\r
6936     switch (LOWORD(wParam)) {\r
6937     case IDM_QuickPaste:\r
6938       {\r
6939         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6940         if (sel.cpMin == sel.cpMax) {\r
6941           MessageBeep(MB_ICONEXCLAMATION);\r
6942           return 0;\r
6943         }\r
6944         SendMessage(hwnd, WM_COPY, 0, 0);\r
6945         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6946         SendMessage(hInput, WM_PASTE, 0, 0);\r
6947         SetFocus(hInput);\r
6948         return 0;\r
6949       }\r
6950     case IDM_Cut:\r
6951       SendMessage(hwnd, WM_CUT, 0, 0);\r
6952       return 0;\r
6953     case IDM_Paste:\r
6954       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6955       return 0;\r
6956     case IDM_Copy:\r
6957       SendMessage(hwnd, WM_COPY, 0, 0);\r
6958       return 0;\r
6959     default:\r
6960       {\r
6961         int i = LOWORD(wParam) - IDM_CommandX;\r
6962         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6963             icsTextMenuEntry[i].command != NULL) {\r
6964           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6965                    icsTextMenuEntry[i].getname,\r
6966                    icsTextMenuEntry[i].immediate);\r
6967           return 0;\r
6968         }\r
6969       }\r
6970       break;\r
6971     }\r
6972     break;\r
6973   }\r
6974   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6975 }\r
6976 \r
6977 WNDPROC consoleInputWindowProc;\r
6978 \r
6979 LRESULT CALLBACK\r
6980 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6981 {\r
6982   char buf[MSG_SIZ];\r
6983   char *p;\r
6984   static BOOL sendNextChar = FALSE;\r
6985   static BOOL quoteNextChar = FALSE;\r
6986   InputSource *is = consoleInputSource;\r
6987   CHARFORMAT cf;\r
6988   CHARRANGE sel;\r
6989 \r
6990   switch (message) {\r
6991   case WM_CHAR:\r
6992     if (!appData.localLineEditing || sendNextChar) {\r
6993       is->buf[0] = (CHAR) wParam;\r
6994       is->count = 1;\r
6995       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6996       sendNextChar = FALSE;\r
6997       return 0;\r
6998     }\r
6999     if (quoteNextChar) {\r
7000       buf[0] = (char) wParam;\r
7001       buf[1] = NULLCHAR;\r
7002       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7003       quoteNextChar = FALSE;\r
7004       return 0;\r
7005     }\r
7006     switch (wParam) {\r
7007     case '\r':   /* Enter key */\r
7008       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7009       if (consoleEcho) SaveInHistory(is->buf);\r
7010       is->buf[is->count++] = '\n';\r
7011       is->buf[is->count] = NULLCHAR;\r
7012       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7013       if (consoleEcho) {\r
7014         ConsoleOutput(is->buf, is->count, TRUE);\r
7015       } else if (appData.localLineEditing) {\r
7016         ConsoleOutput("\n", 1, TRUE);\r
7017       }\r
7018       /* fall thru */\r
7019     case '\033': /* Escape key */\r
7020       SetWindowText(hwnd, "");\r
7021       cf.cbSize = sizeof(CHARFORMAT);\r
7022       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7023       if (consoleEcho) {\r
7024         cf.crTextColor = textAttribs[ColorNormal].color;\r
7025       } else {\r
7026         cf.crTextColor = COLOR_ECHOOFF;\r
7027       }\r
7028       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7029       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7030       return 0;\r
7031     case '\t':   /* Tab key */\r
7032       if (GetKeyState(VK_SHIFT) < 0) {\r
7033         /* shifted */\r
7034         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7035       } else {\r
7036         /* unshifted */\r
7037         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7038         if (buttonDesc[0].hwnd) {\r
7039           SetFocus(buttonDesc[0].hwnd);\r
7040         } else {\r
7041           SetFocus(hwndMain);\r
7042         }\r
7043       }\r
7044       return 0;\r
7045     case '\023': /* Ctrl+S */\r
7046       sendNextChar = TRUE;\r
7047       return 0;\r
7048     case '\021': /* Ctrl+Q */\r
7049       quoteNextChar = TRUE;\r
7050       return 0;\r
7051     JAWS_REPLAY\r
7052     default:\r
7053       break;\r
7054     }\r
7055     break;\r
7056   case WM_KEYDOWN:\r
7057     switch (wParam) {\r
7058     case VK_UP:\r
7059       GetWindowText(hwnd, buf, MSG_SIZ);\r
7060       p = PrevInHistory(buf);\r
7061       if (p != NULL) {\r
7062         SetWindowText(hwnd, p);\r
7063         sel.cpMin = 999999;\r
7064         sel.cpMax = 999999;\r
7065         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7066         return 0;\r
7067       }\r
7068       break;\r
7069     case VK_DOWN:\r
7070       p = NextInHistory();\r
7071       if (p != NULL) {\r
7072         SetWindowText(hwnd, p);\r
7073         sel.cpMin = 999999;\r
7074         sel.cpMax = 999999;\r
7075         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7076         return 0;\r
7077       }\r
7078       break;\r
7079     case VK_HOME:\r
7080     case VK_END:\r
7081       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7082       /* fall thru */\r
7083     case VK_PRIOR:\r
7084     case VK_NEXT:\r
7085       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7086       return 0;\r
7087     }\r
7088     break;\r
7089   case WM_MBUTTONDOWN:\r
7090     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7091       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7092     break;\r
7093   case WM_RBUTTONUP:\r
7094     if (GetKeyState(VK_SHIFT) & ~1) {\r
7095       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7096         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7097     } else {\r
7098       POINT pt;\r
7099       HMENU hmenu;\r
7100       hmenu = LoadMenu(hInst, "InputMenu");\r
7101       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7102       if (sel.cpMin == sel.cpMax) {\r
7103         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7104         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7105       }\r
7106       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7107         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7108       }\r
7109       pt.x = LOWORD(lParam);\r
7110       pt.y = HIWORD(lParam);\r
7111       MenuPopup(hwnd, pt, hmenu, -1);\r
7112     }\r
7113     return 0;\r
7114   case WM_COMMAND:\r
7115     switch (LOWORD(wParam)) { \r
7116     case IDM_Undo:\r
7117       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7118       return 0;\r
7119     case IDM_SelectAll:\r
7120       sel.cpMin = 0;\r
7121       sel.cpMax = -1; /*999999?*/\r
7122       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7123       return 0;\r
7124     case IDM_Cut:\r
7125       SendMessage(hwnd, WM_CUT, 0, 0);\r
7126       return 0;\r
7127     case IDM_Paste:\r
7128       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7129       return 0;\r
7130     case IDM_Copy:\r
7131       SendMessage(hwnd, WM_COPY, 0, 0);\r
7132       return 0;\r
7133     }\r
7134     break;\r
7135   }\r
7136   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7137 }\r
7138 \r
7139 #define CO_MAX  100000\r
7140 #define CO_TRIM   1000\r
7141 \r
7142 LRESULT CALLBACK\r
7143 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7144 {\r
7145   static SnapData sd;\r
7146   HWND hText, hInput;\r
7147   RECT rect;\r
7148   static int sizeX, sizeY;\r
7149   int newSizeX, newSizeY;\r
7150   MINMAXINFO *mmi;\r
7151   WORD wMask;\r
7152 \r
7153   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7154   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7155 \r
7156   switch (message) {\r
7157   case WM_NOTIFY:\r
7158     if (((NMHDR*)lParam)->code == EN_LINK)\r
7159     {\r
7160       ENLINK *pLink = (ENLINK*)lParam;\r
7161       if (pLink->msg == WM_LBUTTONUP)\r
7162       {\r
7163         TEXTRANGE tr;\r
7164 \r
7165         tr.chrg = pLink->chrg;\r
7166         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7167         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7168         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7169         free(tr.lpstrText);\r
7170       }\r
7171     }\r
7172     break;\r
7173   case WM_INITDIALOG: /* message: initialize dialog box */\r
7174     hwndConsole = hDlg;\r
7175     SetFocus(hInput);\r
7176     consoleTextWindowProc = (WNDPROC)\r
7177       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7178     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7179     consoleInputWindowProc = (WNDPROC)\r
7180       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7181     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7182     Colorize(ColorNormal, TRUE);\r
7183     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7184     ChangedConsoleFont();\r
7185     GetClientRect(hDlg, &rect);\r
7186     sizeX = rect.right;\r
7187     sizeY = rect.bottom;\r
7188     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7189         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7190       WINDOWPLACEMENT wp;\r
7191       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7192       wp.length = sizeof(WINDOWPLACEMENT);\r
7193       wp.flags = 0;\r
7194       wp.showCmd = SW_SHOW;\r
7195       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7196       wp.rcNormalPosition.left = wpConsole.x;\r
7197       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7198       wp.rcNormalPosition.top = wpConsole.y;\r
7199       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7200       SetWindowPlacement(hDlg, &wp);\r
7201     }\r
7202 \r
7203    // [HGM] Chessknight's change 2004-07-13\r
7204    else { /* Determine Defaults */\r
7205        WINDOWPLACEMENT wp;\r
7206        wpConsole.x = wpMain.width + 1;\r
7207        wpConsole.y = wpMain.y;\r
7208        wpConsole.width = screenWidth -  wpMain.width;\r
7209        wpConsole.height = wpMain.height;\r
7210        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7211        wp.length = sizeof(WINDOWPLACEMENT);\r
7212        wp.flags = 0;\r
7213        wp.showCmd = SW_SHOW;\r
7214        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7215        wp.rcNormalPosition.left = wpConsole.x;\r
7216        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7217        wp.rcNormalPosition.top = wpConsole.y;\r
7218        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7219        SetWindowPlacement(hDlg, &wp);\r
7220     }\r
7221 \r
7222    // Allow hText to highlight URLs and send notifications on them\r
7223    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7224    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7225    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7226    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7227 \r
7228     return FALSE;\r
7229 \r
7230   case WM_SETFOCUS:\r
7231     SetFocus(hInput);\r
7232     return 0;\r
7233 \r
7234   case WM_CLOSE:\r
7235     ExitEvent(0);\r
7236     /* not reached */\r
7237     break;\r
7238 \r
7239   case WM_SIZE:\r
7240     if (IsIconic(hDlg)) break;\r
7241     newSizeX = LOWORD(lParam);\r
7242     newSizeY = HIWORD(lParam);\r
7243     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7244       RECT rectText, rectInput;\r
7245       POINT pt;\r
7246       int newTextHeight, newTextWidth;\r
7247       GetWindowRect(hText, &rectText);\r
7248       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7249       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7250       if (newTextHeight < 0) {\r
7251         newSizeY += -newTextHeight;\r
7252         newTextHeight = 0;\r
7253       }\r
7254       SetWindowPos(hText, NULL, 0, 0,\r
7255         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7256       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7257       pt.x = rectInput.left;\r
7258       pt.y = rectInput.top + newSizeY - sizeY;\r
7259       ScreenToClient(hDlg, &pt);\r
7260       SetWindowPos(hInput, NULL, \r
7261         pt.x, pt.y, /* needs client coords */   \r
7262         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7263         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7264     }\r
7265     sizeX = newSizeX;\r
7266     sizeY = newSizeY;\r
7267     break;\r
7268 \r
7269   case WM_GETMINMAXINFO:\r
7270     /* Prevent resizing window too small */\r
7271     mmi = (MINMAXINFO *) lParam;\r
7272     mmi->ptMinTrackSize.x = 100;\r
7273     mmi->ptMinTrackSize.y = 100;\r
7274     break;\r
7275 \r
7276   /* [AS] Snapping */\r
7277   case WM_ENTERSIZEMOVE:\r
7278     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7279 \r
7280   case WM_SIZING:\r
7281     return OnSizing( &sd, hDlg, wParam, lParam );\r
7282 \r
7283   case WM_MOVING:\r
7284     return OnMoving( &sd, hDlg, wParam, lParam );\r
7285 \r
7286   case WM_EXITSIZEMOVE:\r
7287         UpdateICSWidth(hText);\r
7288     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7289   }\r
7290 \r
7291   return DefWindowProc(hDlg, message, wParam, lParam);\r
7292 }\r
7293 \r
7294 \r
7295 VOID\r
7296 ConsoleCreate()\r
7297 {\r
7298   HWND hCons;\r
7299   if (hwndConsole) return;\r
7300   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7301   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7302 }\r
7303 \r
7304 \r
7305 VOID\r
7306 ConsoleOutput(char* data, int length, int forceVisible)\r
7307 {\r
7308   HWND hText;\r
7309   int trim, exlen;\r
7310   char *p, *q;\r
7311   char buf[CO_MAX+1];\r
7312   POINT pEnd;\r
7313   RECT rect;\r
7314   static int delayLF = 0;\r
7315   CHARRANGE savesel, sel;\r
7316 \r
7317   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7318   p = data;\r
7319   q = buf;\r
7320   if (delayLF) {\r
7321     *q++ = '\r';\r
7322     *q++ = '\n';\r
7323     delayLF = 0;\r
7324   }\r
7325   while (length--) {\r
7326     if (*p == '\n') {\r
7327       if (*++p) {\r
7328         *q++ = '\r';\r
7329         *q++ = '\n';\r
7330       } else {\r
7331         delayLF = 1;\r
7332       }\r
7333     } else if (*p == '\007') {\r
7334        MyPlaySound(&sounds[(int)SoundBell]);\r
7335        p++;\r
7336     } else {\r
7337       *q++ = *p++;\r
7338     }\r
7339   }\r
7340   *q = NULLCHAR;\r
7341   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7342   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7343   /* Save current selection */\r
7344   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7345   exlen = GetWindowTextLength(hText);\r
7346   /* Find out whether current end of text is visible */\r
7347   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7348   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7349   /* Trim existing text if it's too long */\r
7350   if (exlen + (q - buf) > CO_MAX) {\r
7351     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7352     sel.cpMin = 0;\r
7353     sel.cpMax = trim;\r
7354     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7355     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7356     exlen -= trim;\r
7357     savesel.cpMin -= trim;\r
7358     savesel.cpMax -= trim;\r
7359     if (exlen < 0) exlen = 0;\r
7360     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7361     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7362   }\r
7363   /* Append the new text */\r
7364   sel.cpMin = exlen;\r
7365   sel.cpMax = exlen;\r
7366   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7367   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7368   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7369   if (forceVisible || exlen == 0 ||\r
7370       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7371        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7372     /* Scroll to make new end of text visible if old end of text\r
7373        was visible or new text is an echo of user typein */\r
7374     sel.cpMin = 9999999;\r
7375     sel.cpMax = 9999999;\r
7376     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7377     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7378     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7379     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7380   }\r
7381   if (savesel.cpMax == exlen || forceVisible) {\r
7382     /* Move insert point to new end of text if it was at the old\r
7383        end of text or if the new text is an echo of user typein */\r
7384     sel.cpMin = 9999999;\r
7385     sel.cpMax = 9999999;\r
7386     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7387   } else {\r
7388     /* Restore previous selection */\r
7389     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7390   }\r
7391   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7392 }\r
7393 \r
7394 /*---------*/\r
7395 \r
7396 \r
7397 void\r
7398 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7399 {\r
7400   char buf[100];\r
7401   char *str;\r
7402   COLORREF oldFg, oldBg;\r
7403   HFONT oldFont;\r
7404   RECT rect;\r
7405 \r
7406   if(copyNumber > 1)
7407     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7408 \r
7409   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7410   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7411   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7412 \r
7413   rect.left = x;\r
7414   rect.right = x + squareSize;\r
7415   rect.top  = y;\r
7416   rect.bottom = y + squareSize;\r
7417   str = buf;\r
7418 \r
7419   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7420                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7421              y, ETO_CLIPPED|ETO_OPAQUE,\r
7422              &rect, str, strlen(str), NULL);\r
7423 \r
7424   (void) SetTextColor(hdc, oldFg);\r
7425   (void) SetBkColor(hdc, oldBg);\r
7426   (void) SelectObject(hdc, oldFont);\r
7427 }\r
7428 \r
7429 void\r
7430 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7431               RECT *rect, char *color, char *flagFell)\r
7432 {\r
7433   char buf[100];\r
7434   char *str;\r
7435   COLORREF oldFg, oldBg;\r
7436   HFONT oldFont;\r
7437 \r
7438   if (appData.clockMode) {\r
7439     if (tinyLayout)\r
7440       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7441     else\r
7442       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7443     str = buf;\r
7444   } else {\r
7445     str = color;\r
7446   }\r
7447 \r
7448   if (highlight) {\r
7449     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7450     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7451   } else {\r
7452     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7453     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7454   }\r
7455   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7456 \r
7457   JAWS_SILENCE\r
7458 \r
7459   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7460              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7461              rect, str, strlen(str), NULL);\r
7462   if(logoHeight > 0 && appData.clockMode) {\r
7463       RECT r;\r
7464       str += strlen(color)+2;\r
7465       r.top = rect->top + logoHeight/2;\r
7466       r.left = rect->left;\r
7467       r.right = rect->right;\r
7468       r.bottom = rect->bottom;\r
7469       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7470                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7471                  &r, str, strlen(str), NULL);\r
7472   }\r
7473   (void) SetTextColor(hdc, oldFg);\r
7474   (void) SetBkColor(hdc, oldBg);\r
7475   (void) SelectObject(hdc, oldFont);\r
7476 }\r
7477 \r
7478 \r
7479 int\r
7480 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7481            OVERLAPPED *ovl)\r
7482 {\r
7483   int ok, err;\r
7484 \r
7485   /* [AS]  */\r
7486   if( count <= 0 ) {\r
7487     if (appData.debugMode) {\r
7488       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7489     }\r
7490 \r
7491     return ERROR_INVALID_USER_BUFFER;\r
7492   }\r
7493 \r
7494   ResetEvent(ovl->hEvent);\r
7495   ovl->Offset = ovl->OffsetHigh = 0;\r
7496   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7497   if (ok) {\r
7498     err = NO_ERROR;\r
7499   } else {\r
7500     err = GetLastError();\r
7501     if (err == ERROR_IO_PENDING) {\r
7502       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7503       if (ok)\r
7504         err = NO_ERROR;\r
7505       else\r
7506         err = GetLastError();\r
7507     }\r
7508   }\r
7509   return err;\r
7510 }\r
7511 \r
7512 int\r
7513 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7514             OVERLAPPED *ovl)\r
7515 {\r
7516   int ok, err;\r
7517 \r
7518   ResetEvent(ovl->hEvent);\r
7519   ovl->Offset = ovl->OffsetHigh = 0;\r
7520   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7521   if (ok) {\r
7522     err = NO_ERROR;\r
7523   } else {\r
7524     err = GetLastError();\r
7525     if (err == ERROR_IO_PENDING) {\r
7526       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7527       if (ok)\r
7528         err = NO_ERROR;\r
7529       else\r
7530         err = GetLastError();\r
7531     }\r
7532   }\r
7533   return err;\r
7534 }\r
7535 \r
7536 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7537 void CheckForInputBufferFull( InputSource * is )\r
7538 {\r
7539     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7540         /* Look for end of line */\r
7541         char * p = is->buf;\r
7542         \r
7543         while( p < is->next && *p != '\n' ) {\r
7544             p++;\r
7545         }\r
7546 \r
7547         if( p >= is->next ) {\r
7548             if (appData.debugMode) {\r
7549                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7550             }\r
7551 \r
7552             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7553             is->count = (DWORD) -1;\r
7554             is->next = is->buf;\r
7555         }\r
7556     }\r
7557 }\r
7558 \r
7559 DWORD\r
7560 InputThread(LPVOID arg)\r
7561 {\r
7562   InputSource *is;\r
7563   OVERLAPPED ovl;\r
7564 \r
7565   is = (InputSource *) arg;\r
7566   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7567   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7568   while (is->hThread != NULL) {\r
7569     is->error = DoReadFile(is->hFile, is->next,\r
7570                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7571                            &is->count, &ovl);\r
7572     if (is->error == NO_ERROR) {\r
7573       is->next += is->count;\r
7574     } else {\r
7575       if (is->error == ERROR_BROKEN_PIPE) {\r
7576         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7577         is->count = 0;\r
7578       } else {\r
7579         is->count = (DWORD) -1;\r
7580         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7581         break; \r
7582       }\r
7583     }\r
7584 \r
7585     CheckForInputBufferFull( is );\r
7586 \r
7587     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7588 \r
7589     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7590 \r
7591     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7592   }\r
7593 \r
7594   CloseHandle(ovl.hEvent);\r
7595   CloseHandle(is->hFile);\r
7596 \r
7597   if (appData.debugMode) {\r
7598     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7599   }\r
7600 \r
7601   return 0;\r
7602 }\r
7603 \r
7604 \r
7605 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7606 DWORD\r
7607 NonOvlInputThread(LPVOID arg)\r
7608 {\r
7609   InputSource *is;\r
7610   char *p, *q;\r
7611   int i;\r
7612   char prev;\r
7613 \r
7614   is = (InputSource *) arg;\r
7615   while (is->hThread != NULL) {\r
7616     is->error = ReadFile(is->hFile, is->next,\r
7617                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7618                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7619     if (is->error == NO_ERROR) {\r
7620       /* Change CRLF to LF */\r
7621       if (is->next > is->buf) {\r
7622         p = is->next - 1;\r
7623         i = is->count + 1;\r
7624       } else {\r
7625         p = is->next;\r
7626         i = is->count;\r
7627       }\r
7628       q = p;\r
7629       prev = NULLCHAR;\r
7630       while (i > 0) {\r
7631         if (prev == '\r' && *p == '\n') {\r
7632           *(q-1) = '\n';\r
7633           is->count--;\r
7634         } else { \r
7635           *q++ = *p;\r
7636         }\r
7637         prev = *p++;\r
7638         i--;\r
7639       }\r
7640       *q = NULLCHAR;\r
7641       is->next = q;\r
7642     } else {\r
7643       if (is->error == ERROR_BROKEN_PIPE) {\r
7644         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7645         is->count = 0; \r
7646       } else {\r
7647         is->count = (DWORD) -1;\r
7648       }\r
7649     }\r
7650 \r
7651     CheckForInputBufferFull( is );\r
7652 \r
7653     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7654 \r
7655     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7656 \r
7657     if (is->count < 0) break;  /* Quit on error */\r
7658   }\r
7659   CloseHandle(is->hFile);\r
7660   return 0;\r
7661 }\r
7662 \r
7663 DWORD\r
7664 SocketInputThread(LPVOID arg)\r
7665 {\r
7666   InputSource *is;\r
7667 \r
7668   is = (InputSource *) arg;\r
7669   while (is->hThread != NULL) {\r
7670     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7671     if ((int)is->count == SOCKET_ERROR) {\r
7672       is->count = (DWORD) -1;\r
7673       is->error = WSAGetLastError();\r
7674     } else {\r
7675       is->error = NO_ERROR;\r
7676       is->next += is->count;\r
7677       if (is->count == 0 && is->second == is) {\r
7678         /* End of file on stderr; quit with no message */\r
7679         break;\r
7680       }\r
7681     }\r
7682     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7683 \r
7684     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7685 \r
7686     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7687   }\r
7688   return 0;\r
7689 }\r
7690 \r
7691 VOID\r
7692 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7693 {\r
7694   InputSource *is;\r
7695 \r
7696   is = (InputSource *) lParam;\r
7697   if (is->lineByLine) {\r
7698     /* Feed in lines one by one */\r
7699     char *p = is->buf;\r
7700     char *q = p;\r
7701     while (q < is->next) {\r
7702       if (*q++ == '\n') {\r
7703         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7704         p = q;\r
7705       }\r
7706     }\r
7707     \r
7708     /* Move any partial line to the start of the buffer */\r
7709     q = is->buf;\r
7710     while (p < is->next) {\r
7711       *q++ = *p++;\r
7712     }\r
7713     is->next = q;\r
7714 \r
7715     if (is->error != NO_ERROR || is->count == 0) {\r
7716       /* Notify backend of the error.  Note: If there was a partial\r
7717          line at the end, it is not flushed through. */\r
7718       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7719     }\r
7720   } else {\r
7721     /* Feed in the whole chunk of input at once */\r
7722     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7723     is->next = is->buf;\r
7724   }\r
7725 }\r
7726 \r
7727 /*---------------------------------------------------------------------------*\\r
7728  *\r
7729  *  Menu enables. Used when setting various modes.\r
7730  *\r
7731 \*---------------------------------------------------------------------------*/\r
7732 \r
7733 typedef struct {\r
7734   int item;\r
7735   int flags;\r
7736 } Enables;\r
7737 \r
7738 VOID\r
7739 GreyRevert(Boolean grey)\r
7740 { // [HGM] vari: for retracting variations in local mode\r
7741   HMENU hmenu = GetMenu(hwndMain);\r
7742   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7743   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7744 }\r
7745 \r
7746 VOID\r
7747 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7748 {\r
7749   while (enab->item > 0) {\r
7750     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7751     enab++;\r
7752   }\r
7753 }\r
7754 \r
7755 Enables gnuEnables[] = {\r
7756   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7757   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7763   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7764   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7765   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7766   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7769 \r
7770   // Needed to switch from ncp to GNU mode on Engine Load\r
7771   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7772   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7773   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7774   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7775   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7776   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7777   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7778   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7779   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7780   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7781   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7782   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7783   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7784   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7785   { -1, -1 }\r
7786 };\r
7787 \r
7788 Enables icsEnables[] = {\r
7789   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7797   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7804   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7808   { -1, -1 }\r
7809 };\r
7810 \r
7811 #if ZIPPY\r
7812 Enables zippyEnables[] = {\r
7813   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7814   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7815   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7816   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7817   { -1, -1 }\r
7818 };\r
7819 #endif\r
7820 \r
7821 Enables ncpEnables[] = {\r
7822   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7831   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7832   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7844   { -1, -1 }\r
7845 };\r
7846 \r
7847 Enables trainingOnEnables[] = {\r
7848   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7857   { -1, -1 }\r
7858 };\r
7859 \r
7860 Enables trainingOffEnables[] = {\r
7861   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7868   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7870   { -1, -1 }\r
7871 };\r
7872 \r
7873 /* These modify either ncpEnables or gnuEnables */\r
7874 Enables cmailEnables[] = {\r
7875   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7877   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7878   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7882   { -1, -1 }\r
7883 };\r
7884 \r
7885 Enables machineThinkingEnables[] = {\r
7886   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7899 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7902   { -1, -1 }\r
7903 };\r
7904 \r
7905 Enables userThinkingEnables[] = {\r
7906   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7919 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7922   { -1, -1 }\r
7923 };\r
7924 \r
7925 /*---------------------------------------------------------------------------*\\r
7926  *\r
7927  *  Front-end interface functions exported by XBoard.\r
7928  *  Functions appear in same order as prototypes in frontend.h.\r
7929  * \r
7930 \*---------------------------------------------------------------------------*/\r
7931 VOID\r
7932 CheckMark(UINT item, int state)\r
7933 {\r
7934     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7935 }\r
7936 \r
7937 VOID\r
7938 ModeHighlight()\r
7939 {\r
7940   static UINT prevChecked = 0;\r
7941   static int prevPausing = 0;\r
7942   UINT nowChecked;\r
7943 \r
7944   if (pausing != prevPausing) {\r
7945     prevPausing = pausing;\r
7946     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7947                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7948     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7949   }\r
7950 \r
7951   switch (gameMode) {\r
7952   case BeginningOfGame:\r
7953     if (appData.icsActive)\r
7954       nowChecked = IDM_IcsClient;\r
7955     else if (appData.noChessProgram)\r
7956       nowChecked = IDM_EditGame;\r
7957     else\r
7958       nowChecked = IDM_MachineBlack;\r
7959     break;\r
7960   case MachinePlaysBlack:\r
7961     nowChecked = IDM_MachineBlack;\r
7962     break;\r
7963   case MachinePlaysWhite:\r
7964     nowChecked = IDM_MachineWhite;\r
7965     break;\r
7966   case TwoMachinesPlay:\r
7967     nowChecked = IDM_TwoMachines;\r
7968     break;\r
7969   case AnalyzeMode:\r
7970     nowChecked = IDM_AnalysisMode;\r
7971     break;\r
7972   case AnalyzeFile:\r
7973     nowChecked = IDM_AnalyzeFile;\r
7974     break;\r
7975   case EditGame:\r
7976     nowChecked = IDM_EditGame;\r
7977     break;\r
7978   case PlayFromGameFile:\r
7979     nowChecked = IDM_LoadGame;\r
7980     break;\r
7981   case EditPosition:\r
7982     nowChecked = IDM_EditPosition;\r
7983     break;\r
7984   case Training:\r
7985     nowChecked = IDM_Training;\r
7986     break;\r
7987   case IcsPlayingWhite:\r
7988   case IcsPlayingBlack:\r
7989   case IcsObserving:\r
7990   case IcsIdle:\r
7991     nowChecked = IDM_IcsClient;\r
7992     break;\r
7993   default:\r
7994   case EndOfGame:\r
7995     nowChecked = 0;\r
7996     break;\r
7997   }\r
7998   CheckMark(prevChecked, MF_UNCHECKED);\r
7999   CheckMark(nowChecked, MF_CHECKED);\r
8000   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8001 \r
8002   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8003     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8004                           MF_BYCOMMAND|MF_ENABLED);\r
8005   } else {\r
8006     (void) EnableMenuItem(GetMenu(hwndMain), \r
8007                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8008   }\r
8009 \r
8010   prevChecked = nowChecked;\r
8011 \r
8012   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8013   if (appData.icsActive) {\r
8014        if (appData.icsEngineAnalyze) {\r
8015                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8016        } else {\r
8017                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8018        }\r
8019   }\r
8020   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8021 }\r
8022 \r
8023 VOID\r
8024 SetICSMode()\r
8025 {\r
8026   HMENU hmenu = GetMenu(hwndMain);\r
8027   SetMenuEnables(hmenu, icsEnables);\r
8028   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8029     MF_BYCOMMAND|MF_ENABLED);\r
8030 #if ZIPPY\r
8031   if (appData.zippyPlay) {\r
8032     SetMenuEnables(hmenu, zippyEnables);\r
8033     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8034          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8035           MF_BYCOMMAND|MF_ENABLED);\r
8036   }\r
8037 #endif\r
8038 }\r
8039 \r
8040 VOID\r
8041 SetGNUMode()\r
8042 {\r
8043   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8044 }\r
8045 \r
8046 VOID\r
8047 SetNCPMode()\r
8048 {\r
8049   HMENU hmenu = GetMenu(hwndMain);\r
8050   SetMenuEnables(hmenu, ncpEnables);\r
8051     DrawMenuBar(hwndMain);\r
8052 }\r
8053 \r
8054 VOID\r
8055 SetCmailMode()\r
8056 {\r
8057   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8058 }\r
8059 \r
8060 VOID \r
8061 SetTrainingModeOn()\r
8062 {\r
8063   int i;\r
8064   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8065   for (i = 0; i < N_BUTTONS; i++) {\r
8066     if (buttonDesc[i].hwnd != NULL)\r
8067       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8068   }\r
8069   CommentPopDown();\r
8070 }\r
8071 \r
8072 VOID SetTrainingModeOff()\r
8073 {\r
8074   int i;\r
8075   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8076   for (i = 0; i < N_BUTTONS; i++) {\r
8077     if (buttonDesc[i].hwnd != NULL)\r
8078       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8079   }\r
8080 }\r
8081 \r
8082 \r
8083 VOID\r
8084 SetUserThinkingEnables()\r
8085 {\r
8086   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8087 }\r
8088 \r
8089 VOID\r
8090 SetMachineThinkingEnables()\r
8091 {\r
8092   HMENU hMenu = GetMenu(hwndMain);\r
8093   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8094 \r
8095   SetMenuEnables(hMenu, machineThinkingEnables);\r
8096 \r
8097   if (gameMode == MachinePlaysBlack) {\r
8098     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8099   } else if (gameMode == MachinePlaysWhite) {\r
8100     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8101   } else if (gameMode == TwoMachinesPlay) {\r
8102     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8103   }\r
8104 }\r
8105 \r
8106 \r
8107 VOID\r
8108 DisplayTitle(char *str)\r
8109 {\r
8110   char title[MSG_SIZ], *host;\r
8111   if (str[0] != NULLCHAR) {\r
8112     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8113   } else if (appData.icsActive) {\r
8114     if (appData.icsCommPort[0] != NULLCHAR)\r
8115       host = "ICS";\r
8116     else \r
8117       host = appData.icsHost;\r
8118       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8119   } else if (appData.noChessProgram) {\r
8120     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8121   } else {\r
8122     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8123     strcat(title, ": ");\r
8124     strcat(title, first.tidy);\r
8125   }\r
8126   SetWindowText(hwndMain, title);\r
8127 }\r
8128 \r
8129 \r
8130 VOID\r
8131 DisplayMessage(char *str1, char *str2)\r
8132 {\r
8133   HDC hdc;\r
8134   HFONT oldFont;\r
8135   int remain = MESSAGE_TEXT_MAX - 1;\r
8136   int len;\r
8137 \r
8138   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8139   messageText[0] = NULLCHAR;\r
8140   if (*str1) {\r
8141     len = strlen(str1);\r
8142     if (len > remain) len = remain;\r
8143     strncpy(messageText, str1, len);\r
8144     messageText[len] = NULLCHAR;\r
8145     remain -= len;\r
8146   }\r
8147   if (*str2 && remain >= 2) {\r
8148     if (*str1) {\r
8149       strcat(messageText, "  ");\r
8150       remain -= 2;\r
8151     }\r
8152     len = strlen(str2);\r
8153     if (len > remain) len = remain;\r
8154     strncat(messageText, str2, len);\r
8155   }\r
8156   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8157   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8158 \r
8159   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8160 \r
8161   SAYMACHINEMOVE();\r
8162 \r
8163   hdc = GetDC(hwndMain);\r
8164   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8165   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8166              &messageRect, messageText, strlen(messageText), NULL);\r
8167   (void) SelectObject(hdc, oldFont);\r
8168   (void) ReleaseDC(hwndMain, hdc);\r
8169 }\r
8170 \r
8171 VOID\r
8172 DisplayError(char *str, int error)\r
8173 {\r
8174   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8175   int len;\r
8176 \r
8177   if (error == 0) {\r
8178     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8179   } else {\r
8180     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8181                         NULL, error, LANG_NEUTRAL,\r
8182                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8183     if (len > 0) {\r
8184       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8185     } else {\r
8186       ErrorMap *em = errmap;\r
8187       while (em->err != 0 && em->err != error) em++;\r
8188       if (em->err != 0) {\r
8189         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8190       } else {\r
8191         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8192       }\r
8193     }\r
8194   }\r
8195   \r
8196   ErrorPopUp(_("Error"), buf);\r
8197 }\r
8198 \r
8199 \r
8200 VOID\r
8201 DisplayMoveError(char *str)\r
8202 {\r
8203   fromX = fromY = -1;\r
8204   ClearHighlights();\r
8205   DrawPosition(FALSE, NULL);\r
8206   if (appData.popupMoveErrors) {\r
8207     ErrorPopUp(_("Error"), str);\r
8208   } else {\r
8209     DisplayMessage(str, "");\r
8210     moveErrorMessageUp = TRUE;\r
8211   }\r
8212 }\r
8213 \r
8214 VOID\r
8215 DisplayFatalError(char *str, int error, int exitStatus)\r
8216 {\r
8217   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8218   int len;\r
8219   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8220 \r
8221   if (error != 0) {\r
8222     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8223                         NULL, error, LANG_NEUTRAL,\r
8224                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8225     if (len > 0) {\r
8226       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8227     } else {\r
8228       ErrorMap *em = errmap;\r
8229       while (em->err != 0 && em->err != error) em++;\r
8230       if (em->err != 0) {\r
8231         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8232       } else {\r
8233         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8234       }\r
8235     }\r
8236     str = buf;\r
8237   }\r
8238   if (appData.debugMode) {\r
8239     fprintf(debugFP, "%s: %s\n", label, str);\r
8240   }\r
8241   if (appData.popupExitMessage) {\r
8242     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8243                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8244   }\r
8245   ExitEvent(exitStatus);\r
8246 }\r
8247 \r
8248 \r
8249 VOID\r
8250 DisplayInformation(char *str)\r
8251 {\r
8252   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8253 }\r
8254 \r
8255 \r
8256 VOID\r
8257 DisplayNote(char *str)\r
8258 {\r
8259   ErrorPopUp(_("Note"), str);\r
8260 }\r
8261 \r
8262 \r
8263 typedef struct {\r
8264   char *title, *question, *replyPrefix;\r
8265   ProcRef pr;\r
8266 } QuestionParams;\r
8267 \r
8268 LRESULT CALLBACK\r
8269 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8270 {\r
8271   static QuestionParams *qp;\r
8272   char reply[MSG_SIZ];\r
8273   int len, err;\r
8274 \r
8275   switch (message) {\r
8276   case WM_INITDIALOG:\r
8277     qp = (QuestionParams *) lParam;\r
8278     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8279     Translate(hDlg, DLG_Question);\r
8280     SetWindowText(hDlg, qp->title);\r
8281     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8282     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8283     return FALSE;\r
8284 \r
8285   case WM_COMMAND:\r
8286     switch (LOWORD(wParam)) {\r
8287     case IDOK:\r
8288       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8289       if (*reply) strcat(reply, " ");\r
8290       len = strlen(reply);\r
8291       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8292       strcat(reply, "\n");\r
8293       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8294       EndDialog(hDlg, TRUE);\r
8295       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8296       return TRUE;\r
8297     case IDCANCEL:\r
8298       EndDialog(hDlg, FALSE);\r
8299       return TRUE;\r
8300     default:\r
8301       break;\r
8302     }\r
8303     break;\r
8304   }\r
8305   return FALSE;\r
8306 }\r
8307 \r
8308 VOID\r
8309 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8310 {\r
8311     QuestionParams qp;\r
8312     FARPROC lpProc;\r
8313     \r
8314     qp.title = title;\r
8315     qp.question = question;\r
8316     qp.replyPrefix = replyPrefix;\r
8317     qp.pr = pr;\r
8318     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8319     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8320       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8321     FreeProcInstance(lpProc);\r
8322 }\r
8323 \r
8324 /* [AS] Pick FRC position */\r
8325 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8326 {\r
8327     static int * lpIndexFRC;\r
8328     BOOL index_is_ok;\r
8329     char buf[16];\r
8330 \r
8331     switch( message )\r
8332     {\r
8333     case WM_INITDIALOG:\r
8334         lpIndexFRC = (int *) lParam;\r
8335 \r
8336         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8337         Translate(hDlg, DLG_NewGameFRC);\r
8338 \r
8339         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8340         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8341         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8342         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8343 \r
8344         break;\r
8345 \r
8346     case WM_COMMAND:\r
8347         switch( LOWORD(wParam) ) {\r
8348         case IDOK:\r
8349             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8350             EndDialog( hDlg, 0 );\r
8351             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8352             return TRUE;\r
8353         case IDCANCEL:\r
8354             EndDialog( hDlg, 1 );   \r
8355             return TRUE;\r
8356         case IDC_NFG_Edit:\r
8357             if( HIWORD(wParam) == EN_CHANGE ) {\r
8358                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8359 \r
8360                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8361             }\r
8362             return TRUE;\r
8363         case IDC_NFG_Random:\r
8364           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8365             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8366             return TRUE;\r
8367         }\r
8368 \r
8369         break;\r
8370     }\r
8371 \r
8372     return FALSE;\r
8373 }\r
8374 \r
8375 int NewGameFRC()\r
8376 {\r
8377     int result;\r
8378     int index = appData.defaultFrcPosition;\r
8379     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8380 \r
8381     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8382 \r
8383     if( result == 0 ) {\r
8384         appData.defaultFrcPosition = index;\r
8385     }\r
8386 \r
8387     return result;\r
8388 }\r
8389 \r
8390 /* [AS] Game list options. Refactored by HGM */\r
8391 \r
8392 HWND gameListOptionsDialog;\r
8393 \r
8394 // low-level front-end: clear text edit / list widget\r
8395 void\r
8396 GLT_ClearList()\r
8397 {\r
8398     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8399 }\r
8400 \r
8401 // low-level front-end: clear text edit / list widget\r
8402 void\r
8403 GLT_DeSelectList()\r
8404 {\r
8405     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8406 }\r
8407 \r
8408 // low-level front-end: append line to text edit / list widget\r
8409 void\r
8410 GLT_AddToList( char *name )\r
8411 {\r
8412     if( name != 0 ) {\r
8413             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8414     }\r
8415 }\r
8416 \r
8417 // low-level front-end: get line from text edit / list widget\r
8418 Boolean\r
8419 GLT_GetFromList( int index, char *name )\r
8420 {\r
8421     if( name != 0 ) {\r
8422             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8423                 return TRUE;\r
8424     }\r
8425     return FALSE;\r
8426 }\r
8427 \r
8428 void GLT_MoveSelection( HWND hDlg, int delta )\r
8429 {\r
8430     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8431     int idx2 = idx1 + delta;\r
8432     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8433 \r
8434     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8435         char buf[128];\r
8436 \r
8437         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8438         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8439         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8440         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8441     }\r
8442 }\r
8443 \r
8444 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8445 {\r
8446     switch( message )\r
8447     {\r
8448     case WM_INITDIALOG:\r
8449         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8450         \r
8451         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8452         Translate(hDlg, DLG_GameListOptions);\r
8453 \r
8454         /* Initialize list */\r
8455         GLT_TagsToList( lpUserGLT );\r
8456 \r
8457         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8458 \r
8459         break;\r
8460 \r
8461     case WM_COMMAND:\r
8462         switch( LOWORD(wParam) ) {\r
8463         case IDOK:\r
8464             GLT_ParseList();\r
8465             EndDialog( hDlg, 0 );\r
8466             return TRUE;\r
8467         case IDCANCEL:\r
8468             EndDialog( hDlg, 1 );\r
8469             return TRUE;\r
8470 \r
8471         case IDC_GLT_Default:\r
8472             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8473             return TRUE;\r
8474 \r
8475         case IDC_GLT_Restore:\r
8476             GLT_TagsToList( appData.gameListTags );\r
8477             return TRUE;\r
8478 \r
8479         case IDC_GLT_Up:\r
8480             GLT_MoveSelection( hDlg, -1 );\r
8481             return TRUE;\r
8482 \r
8483         case IDC_GLT_Down:\r
8484             GLT_MoveSelection( hDlg, +1 );\r
8485             return TRUE;\r
8486         }\r
8487 \r
8488         break;\r
8489     }\r
8490 \r
8491     return FALSE;\r
8492 }\r
8493 \r
8494 int GameListOptions()\r
8495 {\r
8496     int result;\r
8497     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8498 \r
8499       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8500 \r
8501     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8502 \r
8503     if( result == 0 ) {\r
8504         /* [AS] Memory leak here! */\r
8505         appData.gameListTags = strdup( lpUserGLT ); \r
8506     }\r
8507 \r
8508     return result;\r
8509 }\r
8510 \r
8511 VOID\r
8512 DisplayIcsInteractionTitle(char *str)\r
8513 {\r
8514   char consoleTitle[MSG_SIZ];\r
8515 \r
8516     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8517   SetWindowText(hwndConsole, consoleTitle);\r
8518 }\r
8519 \r
8520 void\r
8521 DrawPosition(int fullRedraw, Board board)\r
8522 {\r
8523   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8524 }\r
8525 \r
8526 void NotifyFrontendLogin()\r
8527 {\r
8528         if (hwndConsole)\r
8529                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8530 }\r
8531 \r
8532 VOID\r
8533 ResetFrontEnd()\r
8534 {\r
8535   fromX = fromY = -1;\r
8536   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8537     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8538     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8539     dragInfo.lastpos = dragInfo.pos;\r
8540     dragInfo.start.x = dragInfo.start.y = -1;\r
8541     dragInfo.from = dragInfo.start;\r
8542     ReleaseCapture();\r
8543     DrawPosition(TRUE, NULL);\r
8544   }\r
8545   TagsPopDown();\r
8546 }\r
8547 \r
8548 \r
8549 VOID\r
8550 CommentPopUp(char *title, char *str)\r
8551 {\r
8552   HWND hwnd = GetActiveWindow();\r
8553   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8554   SAY(str);\r
8555   SetActiveWindow(hwnd);\r
8556 }\r
8557 \r
8558 VOID\r
8559 CommentPopDown(void)\r
8560 {\r
8561   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8562   if (commentDialog) {\r
8563     ShowWindow(commentDialog, SW_HIDE);\r
8564   }\r
8565   commentUp = FALSE;\r
8566 }\r
8567 \r
8568 VOID\r
8569 EditCommentPopUp(int index, char *title, char *str)\r
8570 {\r
8571   EitherCommentPopUp(index, title, str, TRUE);\r
8572 }\r
8573 \r
8574 \r
8575 VOID\r
8576 RingBell()\r
8577 {\r
8578   MyPlaySound(&sounds[(int)SoundMove]);\r
8579 }\r
8580 \r
8581 VOID PlayIcsWinSound()\r
8582 {\r
8583   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8584 }\r
8585 \r
8586 VOID PlayIcsLossSound()\r
8587 {\r
8588   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8589 }\r
8590 \r
8591 VOID PlayIcsDrawSound()\r
8592 {\r
8593   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8594 }\r
8595 \r
8596 VOID PlayIcsUnfinishedSound()\r
8597 {\r
8598   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8599 }\r
8600 \r
8601 VOID\r
8602 PlayAlarmSound()\r
8603 {\r
8604   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8605 }\r
8606 \r
8607 \r
8608 VOID\r
8609 EchoOn()\r
8610 {\r
8611   HWND hInput;\r
8612   consoleEcho = TRUE;\r
8613   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8614   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8615   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8616 }\r
8617 \r
8618 \r
8619 VOID\r
8620 EchoOff()\r
8621 {\r
8622   CHARFORMAT cf;\r
8623   HWND hInput;\r
8624   consoleEcho = FALSE;\r
8625   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8626   /* This works OK: set text and background both to the same color */\r
8627   cf = consoleCF;\r
8628   cf.crTextColor = COLOR_ECHOOFF;\r
8629   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8630   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8631 }\r
8632 \r
8633 /* No Raw()...? */\r
8634 \r
8635 void Colorize(ColorClass cc, int continuation)\r
8636 {\r
8637   currentColorClass = cc;\r
8638   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8639   consoleCF.crTextColor = textAttribs[cc].color;\r
8640   consoleCF.dwEffects = textAttribs[cc].effects;\r
8641   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8642 }\r
8643 \r
8644 char *\r
8645 UserName()\r
8646 {\r
8647   static char buf[MSG_SIZ];\r
8648   DWORD bufsiz = MSG_SIZ;\r
8649 \r
8650   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8651         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8652   }\r
8653   if (!GetUserName(buf, &bufsiz)) {\r
8654     /*DisplayError("Error getting user name", GetLastError());*/\r
8655     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8656   }\r
8657   return buf;\r
8658 }\r
8659 \r
8660 char *\r
8661 HostName()\r
8662 {\r
8663   static char buf[MSG_SIZ];\r
8664   DWORD bufsiz = MSG_SIZ;\r
8665 \r
8666   if (!GetComputerName(buf, &bufsiz)) {\r
8667     /*DisplayError("Error getting host name", GetLastError());*/\r
8668     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8669   }\r
8670   return buf;\r
8671 }\r
8672 \r
8673 \r
8674 int\r
8675 ClockTimerRunning()\r
8676 {\r
8677   return clockTimerEvent != 0;\r
8678 }\r
8679 \r
8680 int\r
8681 StopClockTimer()\r
8682 {\r
8683   if (clockTimerEvent == 0) return FALSE;\r
8684   KillTimer(hwndMain, clockTimerEvent);\r
8685   clockTimerEvent = 0;\r
8686   return TRUE;\r
8687 }\r
8688 \r
8689 void\r
8690 StartClockTimer(long millisec)\r
8691 {\r
8692   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8693                              (UINT) millisec, NULL);\r
8694 }\r
8695 \r
8696 void\r
8697 DisplayWhiteClock(long timeRemaining, int highlight)\r
8698 {\r
8699   HDC hdc;\r
8700   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8701 \r
8702   if(appData.noGUI) return;\r
8703   hdc = GetDC(hwndMain);\r
8704   if (!IsIconic(hwndMain)) {\r
8705     DisplayAClock(hdc, timeRemaining, highlight, \r
8706                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8707   }\r
8708   if (highlight && iconCurrent == iconBlack) {\r
8709     iconCurrent = iconWhite;\r
8710     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8711     if (IsIconic(hwndMain)) {\r
8712       DrawIcon(hdc, 2, 2, iconCurrent);\r
8713     }\r
8714   }\r
8715   (void) ReleaseDC(hwndMain, hdc);\r
8716   if (hwndConsole)\r
8717     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8718 }\r
8719 \r
8720 void\r
8721 DisplayBlackClock(long timeRemaining, int highlight)\r
8722 {\r
8723   HDC hdc;\r
8724   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8725 \r
8726   if(appData.noGUI) return;\r
8727   hdc = GetDC(hwndMain);\r
8728   if (!IsIconic(hwndMain)) {\r
8729     DisplayAClock(hdc, timeRemaining, highlight, \r
8730                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8731   }\r
8732   if (highlight && iconCurrent == iconWhite) {\r
8733     iconCurrent = iconBlack;\r
8734     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8735     if (IsIconic(hwndMain)) {\r
8736       DrawIcon(hdc, 2, 2, iconCurrent);\r
8737     }\r
8738   }\r
8739   (void) ReleaseDC(hwndMain, hdc);\r
8740   if (hwndConsole)\r
8741     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8742 }\r
8743 \r
8744 \r
8745 int\r
8746 LoadGameTimerRunning()\r
8747 {\r
8748   return loadGameTimerEvent != 0;\r
8749 }\r
8750 \r
8751 int\r
8752 StopLoadGameTimer()\r
8753 {\r
8754   if (loadGameTimerEvent == 0) return FALSE;\r
8755   KillTimer(hwndMain, loadGameTimerEvent);\r
8756   loadGameTimerEvent = 0;\r
8757   return TRUE;\r
8758 }\r
8759 \r
8760 void\r
8761 StartLoadGameTimer(long millisec)\r
8762 {\r
8763   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8764                                 (UINT) millisec, NULL);\r
8765 }\r
8766 \r
8767 void\r
8768 AutoSaveGame()\r
8769 {\r
8770   char *defName;\r
8771   FILE *f;\r
8772   char fileTitle[MSG_SIZ];\r
8773 \r
8774   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8775   f = OpenFileDialog(hwndMain, "a", defName,\r
8776                      appData.oldSaveStyle ? "gam" : "pgn",\r
8777                      GAME_FILT, \r
8778                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8779   if (f != NULL) {\r
8780     SaveGame(f, 0, "");\r
8781     fclose(f);\r
8782   }\r
8783 }\r
8784 \r
8785 \r
8786 void\r
8787 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8788 {\r
8789   if (delayedTimerEvent != 0) {\r
8790     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8791       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8792     }\r
8793     KillTimer(hwndMain, delayedTimerEvent);\r
8794     delayedTimerEvent = 0;\r
8795     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8796     delayedTimerCallback();\r
8797   }\r
8798   delayedTimerCallback = cb;\r
8799   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8800                                 (UINT) millisec, NULL);\r
8801 }\r
8802 \r
8803 DelayedEventCallback\r
8804 GetDelayedEvent()\r
8805 {\r
8806   if (delayedTimerEvent) {\r
8807     return delayedTimerCallback;\r
8808   } else {\r
8809     return NULL;\r
8810   }\r
8811 }\r
8812 \r
8813 void\r
8814 CancelDelayedEvent()\r
8815 {\r
8816   if (delayedTimerEvent) {\r
8817     KillTimer(hwndMain, delayedTimerEvent);\r
8818     delayedTimerEvent = 0;\r
8819   }\r
8820 }\r
8821 \r
8822 DWORD GetWin32Priority(int nice)\r
8823 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8824 /*\r
8825 REALTIME_PRIORITY_CLASS     0x00000100\r
8826 HIGH_PRIORITY_CLASS         0x00000080\r
8827 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8828 NORMAL_PRIORITY_CLASS       0x00000020\r
8829 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8830 IDLE_PRIORITY_CLASS         0x00000040\r
8831 */\r
8832         if (nice < -15) return 0x00000080;\r
8833         if (nice < 0)   return 0x00008000;\r
8834         if (nice == 0)  return 0x00000020;\r
8835         if (nice < 15)  return 0x00004000;\r
8836         return 0x00000040;\r
8837 }\r
8838 \r
8839 /* Start a child process running the given program.\r
8840    The process's standard output can be read from "from", and its\r
8841    standard input can be written to "to".\r
8842    Exit with fatal error if anything goes wrong.\r
8843    Returns an opaque pointer that can be used to destroy the process\r
8844    later.\r
8845 */\r
8846 int\r
8847 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8848 {\r
8849 #define BUFSIZE 4096\r
8850 \r
8851   HANDLE hChildStdinRd, hChildStdinWr,\r
8852     hChildStdoutRd, hChildStdoutWr;\r
8853   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8854   SECURITY_ATTRIBUTES saAttr;\r
8855   BOOL fSuccess;\r
8856   PROCESS_INFORMATION piProcInfo;\r
8857   STARTUPINFO siStartInfo;\r
8858   ChildProc *cp;\r
8859   char buf[MSG_SIZ];\r
8860   DWORD err;\r
8861 \r
8862   if (appData.debugMode) {\r
8863     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8864   }\r
8865 \r
8866   *pr = NoProc;\r
8867 \r
8868   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8869   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8870   saAttr.bInheritHandle = TRUE;\r
8871   saAttr.lpSecurityDescriptor = NULL;\r
8872 \r
8873   /*\r
8874    * The steps for redirecting child's STDOUT:\r
8875    *     1. Create anonymous pipe to be STDOUT for child.\r
8876    *     2. Create a noninheritable duplicate of read handle,\r
8877    *         and close the inheritable read handle.\r
8878    */\r
8879 \r
8880   /* Create a pipe for the child's STDOUT. */\r
8881   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8882     return GetLastError();\r
8883   }\r
8884 \r
8885   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8886   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8887                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8888                              FALSE,     /* not inherited */\r
8889                              DUPLICATE_SAME_ACCESS);\r
8890   if (! fSuccess) {\r
8891     return GetLastError();\r
8892   }\r
8893   CloseHandle(hChildStdoutRd);\r
8894 \r
8895   /*\r
8896    * The steps for redirecting child's STDIN:\r
8897    *     1. Create anonymous pipe to be STDIN for child.\r
8898    *     2. Create a noninheritable duplicate of write handle,\r
8899    *         and close the inheritable write handle.\r
8900    */\r
8901 \r
8902   /* Create a pipe for the child's STDIN. */\r
8903   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8904     return GetLastError();\r
8905   }\r
8906 \r
8907   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8908   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8909                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8910                              FALSE,     /* not inherited */\r
8911                              DUPLICATE_SAME_ACCESS);\r
8912   if (! fSuccess) {\r
8913     return GetLastError();\r
8914   }\r
8915   CloseHandle(hChildStdinWr);\r
8916 \r
8917   /* Arrange to (1) look in dir for the child .exe file, and\r
8918    * (2) have dir be the child's working directory.  Interpret\r
8919    * dir relative to the directory WinBoard loaded from. */\r
8920   GetCurrentDirectory(MSG_SIZ, buf);\r
8921   SetCurrentDirectory(installDir);\r
8922   SetCurrentDirectory(dir);\r
8923 \r
8924   /* Now create the child process. */\r
8925 \r
8926   siStartInfo.cb = sizeof(STARTUPINFO);\r
8927   siStartInfo.lpReserved = NULL;\r
8928   siStartInfo.lpDesktop = NULL;\r
8929   siStartInfo.lpTitle = NULL;\r
8930   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8931   siStartInfo.cbReserved2 = 0;\r
8932   siStartInfo.lpReserved2 = NULL;\r
8933   siStartInfo.hStdInput = hChildStdinRd;\r
8934   siStartInfo.hStdOutput = hChildStdoutWr;\r
8935   siStartInfo.hStdError = hChildStdoutWr;\r
8936 \r
8937   fSuccess = CreateProcess(NULL,\r
8938                            cmdLine,        /* command line */\r
8939                            NULL,           /* process security attributes */\r
8940                            NULL,           /* primary thread security attrs */\r
8941                            TRUE,           /* handles are inherited */\r
8942                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8943                            NULL,           /* use parent's environment */\r
8944                            NULL,\r
8945                            &siStartInfo, /* STARTUPINFO pointer */\r
8946                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8947 \r
8948   err = GetLastError();\r
8949   SetCurrentDirectory(buf); /* return to prev directory */\r
8950   if (! fSuccess) {\r
8951     return err;\r
8952   }\r
8953 \r
8954   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8955     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8956     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8957   }\r
8958 \r
8959   /* Close the handles we don't need in the parent */\r
8960   CloseHandle(piProcInfo.hThread);\r
8961   CloseHandle(hChildStdinRd);\r
8962   CloseHandle(hChildStdoutWr);\r
8963 \r
8964   /* Prepare return value */\r
8965   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8966   cp->kind = CPReal;\r
8967   cp->hProcess = piProcInfo.hProcess;\r
8968   cp->pid = piProcInfo.dwProcessId;\r
8969   cp->hFrom = hChildStdoutRdDup;\r
8970   cp->hTo = hChildStdinWrDup;\r
8971 \r
8972   *pr = (void *) cp;\r
8973 \r
8974   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8975      2000 where engines sometimes don't see the initial command(s)\r
8976      from WinBoard and hang.  I don't understand how that can happen,\r
8977      but the Sleep is harmless, so I've put it in.  Others have also\r
8978      reported what may be the same problem, so hopefully this will fix\r
8979      it for them too.  */\r
8980   Sleep(500);\r
8981 \r
8982   return NO_ERROR;\r
8983 }\r
8984 \r
8985 \r
8986 void\r
8987 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8988 {\r
8989   ChildProc *cp; int result;\r
8990 \r
8991   cp = (ChildProc *) pr;\r
8992   if (cp == NULL) return;\r
8993 \r
8994   switch (cp->kind) {\r
8995   case CPReal:\r
8996     /* TerminateProcess is considered harmful, so... */\r
8997     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8998     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8999     /* The following doesn't work because the chess program\r
9000        doesn't "have the same console" as WinBoard.  Maybe\r
9001        we could arrange for this even though neither WinBoard\r
9002        nor the chess program uses a console for stdio? */\r
9003     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9004 \r
9005     /* [AS] Special termination modes for misbehaving programs... */\r
9006     if( signal == 9 ) { \r
9007         result = TerminateProcess( cp->hProcess, 0 );\r
9008 \r
9009         if ( appData.debugMode) {\r
9010             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9011         }\r
9012     }\r
9013     else if( signal == 10 ) {\r
9014         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9015 \r
9016         if( dw != WAIT_OBJECT_0 ) {\r
9017             result = TerminateProcess( cp->hProcess, 0 );\r
9018 \r
9019             if ( appData.debugMode) {\r
9020                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9021             }\r
9022 \r
9023         }\r
9024     }\r
9025 \r
9026     CloseHandle(cp->hProcess);\r
9027     break;\r
9028 \r
9029   case CPComm:\r
9030     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9031     break;\r
9032 \r
9033   case CPSock:\r
9034     closesocket(cp->sock);\r
9035     WSACleanup();\r
9036     break;\r
9037 \r
9038   case CPRcmd:\r
9039     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9040     closesocket(cp->sock);\r
9041     closesocket(cp->sock2);\r
9042     WSACleanup();\r
9043     break;\r
9044   }\r
9045   free(cp);\r
9046 }\r
9047 \r
9048 void\r
9049 InterruptChildProcess(ProcRef pr)\r
9050 {\r
9051   ChildProc *cp;\r
9052 \r
9053   cp = (ChildProc *) pr;\r
9054   if (cp == NULL) return;\r
9055   switch (cp->kind) {\r
9056   case CPReal:\r
9057     /* The following doesn't work because the chess program\r
9058        doesn't "have the same console" as WinBoard.  Maybe\r
9059        we could arrange for this even though neither WinBoard\r
9060        nor the chess program uses a console for stdio */\r
9061     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9062     break;\r
9063 \r
9064   case CPComm:\r
9065   case CPSock:\r
9066     /* Can't interrupt */\r
9067     break;\r
9068 \r
9069   case CPRcmd:\r
9070     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9071     break;\r
9072   }\r
9073 }\r
9074 \r
9075 \r
9076 int\r
9077 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9078 {\r
9079   char cmdLine[MSG_SIZ];\r
9080 \r
9081   if (port[0] == NULLCHAR) {\r
9082     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9083   } else {\r
9084     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9085   }\r
9086   return StartChildProcess(cmdLine, "", pr);\r
9087 }\r
9088 \r
9089 \r
9090 /* Code to open TCP sockets */\r
9091 \r
9092 int\r
9093 OpenTCP(char *host, char *port, ProcRef *pr)\r
9094 {\r
9095   ChildProc *cp;\r
9096   int err;\r
9097   SOCKET s;\r
9098   struct sockaddr_in sa, mysa;\r
9099   struct hostent FAR *hp;\r
9100   unsigned short uport;\r
9101   WORD wVersionRequested;\r
9102   WSADATA wsaData;\r
9103 \r
9104   /* Initialize socket DLL */\r
9105   wVersionRequested = MAKEWORD(1, 1);\r
9106   err = WSAStartup(wVersionRequested, &wsaData);\r
9107   if (err != 0) return err;\r
9108 \r
9109   /* Make socket */\r
9110   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9111     err = WSAGetLastError();\r
9112     WSACleanup();\r
9113     return err;\r
9114   }\r
9115 \r
9116   /* Bind local address using (mostly) don't-care values.\r
9117    */\r
9118   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9119   mysa.sin_family = AF_INET;\r
9120   mysa.sin_addr.s_addr = INADDR_ANY;\r
9121   uport = (unsigned short) 0;\r
9122   mysa.sin_port = htons(uport);\r
9123   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9124       == SOCKET_ERROR) {\r
9125     err = WSAGetLastError();\r
9126     WSACleanup();\r
9127     return err;\r
9128   }\r
9129 \r
9130   /* Resolve remote host name */\r
9131   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9132   if (!(hp = gethostbyname(host))) {\r
9133     unsigned int b0, b1, b2, b3;\r
9134 \r
9135     err = WSAGetLastError();\r
9136 \r
9137     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9138       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9139       hp->h_addrtype = AF_INET;\r
9140       hp->h_length = 4;\r
9141       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9142       hp->h_addr_list[0] = (char *) malloc(4);\r
9143       hp->h_addr_list[0][0] = (char) b0;\r
9144       hp->h_addr_list[0][1] = (char) b1;\r
9145       hp->h_addr_list[0][2] = (char) b2;\r
9146       hp->h_addr_list[0][3] = (char) b3;\r
9147     } else {\r
9148       WSACleanup();\r
9149       return err;\r
9150     }\r
9151   }\r
9152   sa.sin_family = hp->h_addrtype;\r
9153   uport = (unsigned short) atoi(port);\r
9154   sa.sin_port = htons(uport);\r
9155   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9156 \r
9157   /* Make connection */\r
9158   if (connect(s, (struct sockaddr *) &sa,\r
9159               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9160     err = WSAGetLastError();\r
9161     WSACleanup();\r
9162     return err;\r
9163   }\r
9164 \r
9165   /* Prepare return value */\r
9166   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9167   cp->kind = CPSock;\r
9168   cp->sock = s;\r
9169   *pr = (ProcRef *) cp;\r
9170 \r
9171   return NO_ERROR;\r
9172 }\r
9173 \r
9174 int\r
9175 OpenCommPort(char *name, ProcRef *pr)\r
9176 {\r
9177   HANDLE h;\r
9178   COMMTIMEOUTS ct;\r
9179   ChildProc *cp;\r
9180   char fullname[MSG_SIZ];\r
9181 \r
9182   if (*name != '\\')\r
9183     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9184   else\r
9185     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9186 \r
9187   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9188                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9189   if (h == (HANDLE) -1) {\r
9190     return GetLastError();\r
9191   }\r
9192   hCommPort = h;\r
9193 \r
9194   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9195 \r
9196   /* Accumulate characters until a 100ms pause, then parse */\r
9197   ct.ReadIntervalTimeout = 100;\r
9198   ct.ReadTotalTimeoutMultiplier = 0;\r
9199   ct.ReadTotalTimeoutConstant = 0;\r
9200   ct.WriteTotalTimeoutMultiplier = 0;\r
9201   ct.WriteTotalTimeoutConstant = 0;\r
9202   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9203 \r
9204   /* Prepare return value */\r
9205   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9206   cp->kind = CPComm;\r
9207   cp->hFrom = h;\r
9208   cp->hTo = h;\r
9209   *pr = (ProcRef *) cp;\r
9210 \r
9211   return NO_ERROR;\r
9212 }\r
9213 \r
9214 int\r
9215 OpenLoopback(ProcRef *pr)\r
9216 {\r
9217   DisplayFatalError(_("Not implemented"), 0, 1);\r
9218   return NO_ERROR;\r
9219 }\r
9220 \r
9221 \r
9222 int\r
9223 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9224 {\r
9225   ChildProc *cp;\r
9226   int err;\r
9227   SOCKET s, s2, s3;\r
9228   struct sockaddr_in sa, mysa;\r
9229   struct hostent FAR *hp;\r
9230   unsigned short uport;\r
9231   WORD wVersionRequested;\r
9232   WSADATA wsaData;\r
9233   int fromPort;\r
9234   char stderrPortStr[MSG_SIZ];\r
9235 \r
9236   /* Initialize socket DLL */\r
9237   wVersionRequested = MAKEWORD(1, 1);\r
9238   err = WSAStartup(wVersionRequested, &wsaData);\r
9239   if (err != 0) return err;\r
9240 \r
9241   /* Resolve remote host name */\r
9242   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9243   if (!(hp = gethostbyname(host))) {\r
9244     unsigned int b0, b1, b2, b3;\r
9245 \r
9246     err = WSAGetLastError();\r
9247 \r
9248     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9249       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9250       hp->h_addrtype = AF_INET;\r
9251       hp->h_length = 4;\r
9252       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9253       hp->h_addr_list[0] = (char *) malloc(4);\r
9254       hp->h_addr_list[0][0] = (char) b0;\r
9255       hp->h_addr_list[0][1] = (char) b1;\r
9256       hp->h_addr_list[0][2] = (char) b2;\r
9257       hp->h_addr_list[0][3] = (char) b3;\r
9258     } else {\r
9259       WSACleanup();\r
9260       return err;\r
9261     }\r
9262   }\r
9263   sa.sin_family = hp->h_addrtype;\r
9264   uport = (unsigned short) 514;\r
9265   sa.sin_port = htons(uport);\r
9266   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9267 \r
9268   /* Bind local socket to unused "privileged" port address\r
9269    */\r
9270   s = INVALID_SOCKET;\r
9271   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9272   mysa.sin_family = AF_INET;\r
9273   mysa.sin_addr.s_addr = INADDR_ANY;\r
9274   for (fromPort = 1023;; fromPort--) {\r
9275     if (fromPort < 0) {\r
9276       WSACleanup();\r
9277       return WSAEADDRINUSE;\r
9278     }\r
9279     if (s == INVALID_SOCKET) {\r
9280       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9281         err = WSAGetLastError();\r
9282         WSACleanup();\r
9283         return err;\r
9284       }\r
9285     }\r
9286     uport = (unsigned short) fromPort;\r
9287     mysa.sin_port = htons(uport);\r
9288     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9289         == SOCKET_ERROR) {\r
9290       err = WSAGetLastError();\r
9291       if (err == WSAEADDRINUSE) continue;\r
9292       WSACleanup();\r
9293       return err;\r
9294     }\r
9295     if (connect(s, (struct sockaddr *) &sa,\r
9296       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9297       err = WSAGetLastError();\r
9298       if (err == WSAEADDRINUSE) {\r
9299         closesocket(s);\r
9300         s = -1;\r
9301         continue;\r
9302       }\r
9303       WSACleanup();\r
9304       return err;\r
9305     }\r
9306     break;\r
9307   }\r
9308 \r
9309   /* Bind stderr local socket to unused "privileged" port address\r
9310    */\r
9311   s2 = INVALID_SOCKET;\r
9312   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9313   mysa.sin_family = AF_INET;\r
9314   mysa.sin_addr.s_addr = INADDR_ANY;\r
9315   for (fromPort = 1023;; fromPort--) {\r
9316     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9317     if (fromPort < 0) {\r
9318       (void) closesocket(s);\r
9319       WSACleanup();\r
9320       return WSAEADDRINUSE;\r
9321     }\r
9322     if (s2 == INVALID_SOCKET) {\r
9323       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9324         err = WSAGetLastError();\r
9325         closesocket(s);\r
9326         WSACleanup();\r
9327         return err;\r
9328       }\r
9329     }\r
9330     uport = (unsigned short) fromPort;\r
9331     mysa.sin_port = htons(uport);\r
9332     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9333         == SOCKET_ERROR) {\r
9334       err = WSAGetLastError();\r
9335       if (err == WSAEADDRINUSE) continue;\r
9336       (void) closesocket(s);\r
9337       WSACleanup();\r
9338       return err;\r
9339     }\r
9340     if (listen(s2, 1) == SOCKET_ERROR) {\r
9341       err = WSAGetLastError();\r
9342       if (err == WSAEADDRINUSE) {\r
9343         closesocket(s2);\r
9344         s2 = INVALID_SOCKET;\r
9345         continue;\r
9346       }\r
9347       (void) closesocket(s);\r
9348       (void) closesocket(s2);\r
9349       WSACleanup();\r
9350       return err;\r
9351     }\r
9352     break;\r
9353   }\r
9354   prevStderrPort = fromPort; // remember port used\r
9355   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9356 \r
9357   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9358     err = WSAGetLastError();\r
9359     (void) closesocket(s);\r
9360     (void) closesocket(s2);\r
9361     WSACleanup();\r
9362     return err;\r
9363   }\r
9364 \r
9365   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9366     err = WSAGetLastError();\r
9367     (void) closesocket(s);\r
9368     (void) closesocket(s2);\r
9369     WSACleanup();\r
9370     return err;\r
9371   }\r
9372   if (*user == NULLCHAR) user = UserName();\r
9373   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9374     err = WSAGetLastError();\r
9375     (void) closesocket(s);\r
9376     (void) closesocket(s2);\r
9377     WSACleanup();\r
9378     return err;\r
9379   }\r
9380   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9381     err = WSAGetLastError();\r
9382     (void) closesocket(s);\r
9383     (void) closesocket(s2);\r
9384     WSACleanup();\r
9385     return err;\r
9386   }\r
9387 \r
9388   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9389     err = WSAGetLastError();\r
9390     (void) closesocket(s);\r
9391     (void) closesocket(s2);\r
9392     WSACleanup();\r
9393     return err;\r
9394   }\r
9395   (void) closesocket(s2);  /* Stop listening */\r
9396 \r
9397   /* Prepare return value */\r
9398   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9399   cp->kind = CPRcmd;\r
9400   cp->sock = s;\r
9401   cp->sock2 = s3;\r
9402   *pr = (ProcRef *) cp;\r
9403 \r
9404   return NO_ERROR;\r
9405 }\r
9406 \r
9407 \r
9408 InputSourceRef\r
9409 AddInputSource(ProcRef pr, int lineByLine,\r
9410                InputCallback func, VOIDSTAR closure)\r
9411 {\r
9412   InputSource *is, *is2 = NULL;\r
9413   ChildProc *cp = (ChildProc *) pr;\r
9414 \r
9415   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9416   is->lineByLine = lineByLine;\r
9417   is->func = func;\r
9418   is->closure = closure;\r
9419   is->second = NULL;\r
9420   is->next = is->buf;\r
9421   if (pr == NoProc) {\r
9422     is->kind = CPReal;\r
9423     consoleInputSource = is;\r
9424   } else {\r
9425     is->kind = cp->kind;\r
9426     /* \r
9427         [AS] Try to avoid a race condition if the thread is given control too early:\r
9428         we create all threads suspended so that the is->hThread variable can be\r
9429         safely assigned, then let the threads start with ResumeThread.\r
9430     */\r
9431     switch (cp->kind) {\r
9432     case CPReal:\r
9433       is->hFile = cp->hFrom;\r
9434       cp->hFrom = NULL; /* now owned by InputThread */\r
9435       is->hThread =\r
9436         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9437                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9438       break;\r
9439 \r
9440     case CPComm:\r
9441       is->hFile = cp->hFrom;\r
9442       cp->hFrom = NULL; /* now owned by InputThread */\r
9443       is->hThread =\r
9444         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9445                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9446       break;\r
9447 \r
9448     case CPSock:\r
9449       is->sock = cp->sock;\r
9450       is->hThread =\r
9451         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9452                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9453       break;\r
9454 \r
9455     case CPRcmd:\r
9456       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9457       *is2 = *is;\r
9458       is->sock = cp->sock;\r
9459       is->second = is2;\r
9460       is2->sock = cp->sock2;\r
9461       is2->second = is2;\r
9462       is->hThread =\r
9463         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9464                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9465       is2->hThread =\r
9466         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9467                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9468       break;\r
9469     }\r
9470 \r
9471     if( is->hThread != NULL ) {\r
9472         ResumeThread( is->hThread );\r
9473     }\r
9474 \r
9475     if( is2 != NULL && is2->hThread != NULL ) {\r
9476         ResumeThread( is2->hThread );\r
9477     }\r
9478   }\r
9479 \r
9480   return (InputSourceRef) is;\r
9481 }\r
9482 \r
9483 void\r
9484 RemoveInputSource(InputSourceRef isr)\r
9485 {\r
9486   InputSource *is;\r
9487 \r
9488   is = (InputSource *) isr;\r
9489   is->hThread = NULL;  /* tell thread to stop */\r
9490   CloseHandle(is->hThread);\r
9491   if (is->second != NULL) {\r
9492     is->second->hThread = NULL;\r
9493     CloseHandle(is->second->hThread);\r
9494   }\r
9495 }\r
9496 \r
9497 int no_wrap(char *message, int count)\r
9498 {\r
9499     ConsoleOutput(message, count, FALSE);\r
9500     return count;\r
9501 }\r
9502 \r
9503 int\r
9504 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9505 {\r
9506   DWORD dOutCount;\r
9507   int outCount = SOCKET_ERROR;\r
9508   ChildProc *cp = (ChildProc *) pr;\r
9509   static OVERLAPPED ovl;\r
9510   static int line = 0;\r
9511 \r
9512   if (pr == NoProc)\r
9513   {\r
9514     if (appData.noJoin || !appData.useInternalWrap)\r
9515       return no_wrap(message, count);\r
9516     else\r
9517     {\r
9518       int width = get_term_width();\r
9519       int len = wrap(NULL, message, count, width, &line);\r
9520       char *msg = malloc(len);\r
9521       int dbgchk;\r
9522 \r
9523       if (!msg)\r
9524         return no_wrap(message, count);\r
9525       else\r
9526       {\r
9527         dbgchk = wrap(msg, message, count, width, &line);\r
9528         if (dbgchk != len && appData.debugMode)\r
9529             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9530         ConsoleOutput(msg, len, FALSE);\r
9531         free(msg);\r
9532         return len;\r
9533       }\r
9534     }\r
9535   }\r
9536 \r
9537   if (ovl.hEvent == NULL) {\r
9538     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9539   }\r
9540   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9541 \r
9542   switch (cp->kind) {\r
9543   case CPSock:\r
9544   case CPRcmd:\r
9545     outCount = send(cp->sock, message, count, 0);\r
9546     if (outCount == SOCKET_ERROR) {\r
9547       *outError = WSAGetLastError();\r
9548     } else {\r
9549       *outError = NO_ERROR;\r
9550     }\r
9551     break;\r
9552 \r
9553   case CPReal:\r
9554     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9555                   &dOutCount, NULL)) {\r
9556       *outError = NO_ERROR;\r
9557       outCount = (int) dOutCount;\r
9558     } else {\r
9559       *outError = GetLastError();\r
9560     }\r
9561     break;\r
9562 \r
9563   case CPComm:\r
9564     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9565                             &dOutCount, &ovl);\r
9566     if (*outError == NO_ERROR) {\r
9567       outCount = (int) dOutCount;\r
9568     }\r
9569     break;\r
9570   }\r
9571   return outCount;\r
9572 }\r
9573 \r
9574 int\r
9575 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9576                        long msdelay)\r
9577 {\r
9578   /* Ignore delay, not implemented for WinBoard */\r
9579   return OutputToProcess(pr, message, count, outError);\r
9580 }\r
9581 \r
9582 \r
9583 void\r
9584 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9585                         char *buf, int count, int error)\r
9586 {\r
9587   DisplayFatalError(_("Not implemented"), 0, 1);\r
9588 }\r
9589 \r
9590 /* see wgamelist.c for Game List functions */\r
9591 /* see wedittags.c for Edit Tags functions */\r
9592 \r
9593 \r
9594 VOID\r
9595 ICSInitScript()\r
9596 {\r
9597   FILE *f;\r
9598   char buf[MSG_SIZ];\r
9599   char *dummy;\r
9600 \r
9601   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9602     f = fopen(buf, "r");\r
9603     if (f != NULL) {\r
9604       ProcessICSInitScript(f);\r
9605       fclose(f);\r
9606     }\r
9607   }\r
9608 }\r
9609 \r
9610 \r
9611 VOID\r
9612 StartAnalysisClock()\r
9613 {\r
9614   if (analysisTimerEvent) return;\r
9615   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9616                                         (UINT) 2000, NULL);\r
9617 }\r
9618 \r
9619 VOID\r
9620 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9621 {\r
9622   highlightInfo.sq[0].x = fromX;\r
9623   highlightInfo.sq[0].y = fromY;\r
9624   highlightInfo.sq[1].x = toX;\r
9625   highlightInfo.sq[1].y = toY;\r
9626 }\r
9627 \r
9628 VOID\r
9629 ClearHighlights()\r
9630 {\r
9631   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9632     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9633 }\r
9634 \r
9635 VOID\r
9636 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9637 {\r
9638   premoveHighlightInfo.sq[0].x = fromX;\r
9639   premoveHighlightInfo.sq[0].y = fromY;\r
9640   premoveHighlightInfo.sq[1].x = toX;\r
9641   premoveHighlightInfo.sq[1].y = toY;\r
9642 }\r
9643 \r
9644 VOID\r
9645 ClearPremoveHighlights()\r
9646 {\r
9647   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9648     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9649 }\r
9650 \r
9651 VOID\r
9652 ShutDownFrontEnd()\r
9653 {\r
9654   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9655   DeleteClipboardTempFiles();\r
9656 }\r
9657 \r
9658 void\r
9659 BoardToTop()\r
9660 {\r
9661     if (IsIconic(hwndMain))\r
9662       ShowWindow(hwndMain, SW_RESTORE);\r
9663 \r
9664     SetActiveWindow(hwndMain);\r
9665 }\r
9666 \r
9667 /*\r
9668  * Prototypes for animation support routines\r
9669  */\r
9670 static void ScreenSquare(int column, int row, POINT * pt);\r
9671 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9672      POINT frames[], int * nFrames);\r
9673 \r
9674 \r
9675 #define kFactor 4\r
9676 \r
9677 void\r
9678 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9679 {       // [HGM] atomic: animate blast wave\r
9680         int i;\r
9681 \r
9682         explodeInfo.fromX = fromX;\r
9683         explodeInfo.fromY = fromY;\r
9684         explodeInfo.toX = toX;\r
9685         explodeInfo.toY = toY;\r
9686         for(i=1; i<4*kFactor; i++) {\r
9687             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9688             DrawPosition(FALSE, board);\r
9689             Sleep(appData.animSpeed);\r
9690         }\r
9691         explodeInfo.radius = 0;\r
9692         DrawPosition(TRUE, board);\r
9693 }\r
9694 \r
9695 void\r
9696 AnimateMove(board, fromX, fromY, toX, toY)\r
9697      Board board;\r
9698      int fromX;\r
9699      int fromY;\r
9700      int toX;\r
9701      int toY;\r
9702 {\r
9703   ChessSquare piece;\r
9704   POINT start, finish, mid;\r
9705   POINT frames[kFactor * 2 + 1];\r
9706   int nFrames, n;\r
9707 \r
9708   if (!appData.animate) return;\r
9709   if (doingSizing) return;\r
9710   if (fromY < 0 || fromX < 0) return;\r
9711   piece = board[fromY][fromX];\r
9712   if (piece >= EmptySquare) return;\r
9713 \r
9714   ScreenSquare(fromX, fromY, &start);\r
9715   ScreenSquare(toX, toY, &finish);\r
9716 \r
9717   /* All moves except knight jumps move in straight line */\r
9718   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9719     mid.x = start.x + (finish.x - start.x) / 2;\r
9720     mid.y = start.y + (finish.y - start.y) / 2;\r
9721   } else {\r
9722     /* Knight: make straight movement then diagonal */\r
9723     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9724        mid.x = start.x + (finish.x - start.x) / 2;\r
9725        mid.y = start.y;\r
9726      } else {\r
9727        mid.x = start.x;\r
9728        mid.y = start.y + (finish.y - start.y) / 2;\r
9729      }\r
9730   }\r
9731   \r
9732   /* Don't use as many frames for very short moves */\r
9733   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9734     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9735   else\r
9736     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9737 \r
9738   animInfo.from.x = fromX;\r
9739   animInfo.from.y = fromY;\r
9740   animInfo.to.x = toX;\r
9741   animInfo.to.y = toY;\r
9742   animInfo.lastpos = start;\r
9743   animInfo.piece = piece;\r
9744   for (n = 0; n < nFrames; n++) {\r
9745     animInfo.pos = frames[n];\r
9746     DrawPosition(FALSE, NULL);\r
9747     animInfo.lastpos = animInfo.pos;\r
9748     Sleep(appData.animSpeed);\r
9749   }\r
9750   animInfo.pos = finish;\r
9751   DrawPosition(FALSE, NULL);\r
9752   animInfo.piece = EmptySquare;\r
9753   Explode(board, fromX, fromY, toX, toY);\r
9754 }\r
9755 \r
9756 /*      Convert board position to corner of screen rect and color       */\r
9757 \r
9758 static void\r
9759 ScreenSquare(column, row, pt)\r
9760      int column; int row; POINT * pt;\r
9761 {\r
9762   if (flipView) {\r
9763     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9764     pt->y = lineGap + row * (squareSize + lineGap);\r
9765   } else {\r
9766     pt->x = lineGap + column * (squareSize + lineGap);\r
9767     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9768   }\r
9769 }\r
9770 \r
9771 /*      Generate a series of frame coords from start->mid->finish.\r
9772         The movement rate doubles until the half way point is\r
9773         reached, then halves back down to the final destination,\r
9774         which gives a nice slow in/out effect. The algorithmn\r
9775         may seem to generate too many intermediates for short\r
9776         moves, but remember that the purpose is to attract the\r
9777         viewers attention to the piece about to be moved and\r
9778         then to where it ends up. Too few frames would be less\r
9779         noticeable.                                             */\r
9780 \r
9781 static void\r
9782 Tween(start, mid, finish, factor, frames, nFrames)\r
9783      POINT * start; POINT * mid;\r
9784      POINT * finish; int factor;\r
9785      POINT frames[]; int * nFrames;\r
9786 {\r
9787   int n, fraction = 1, count = 0;\r
9788 \r
9789   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9790   for (n = 0; n < factor; n++)\r
9791     fraction *= 2;\r
9792   for (n = 0; n < factor; n++) {\r
9793     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9794     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9795     count ++;\r
9796     fraction = fraction / 2;\r
9797   }\r
9798   \r
9799   /* Midpoint */\r
9800   frames[count] = *mid;\r
9801   count ++;\r
9802   \r
9803   /* Slow out, stepping 1/2, then 1/4, ... */\r
9804   fraction = 2;\r
9805   for (n = 0; n < factor; n++) {\r
9806     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9807     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9808     count ++;\r
9809     fraction = fraction * 2;\r
9810   }\r
9811   *nFrames = count;\r
9812 }\r
9813 \r
9814 void\r
9815 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9816 {\r
9817     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9818 \r
9819     EvalGraphSet( first, last, current, pvInfoList );\r
9820 }\r
9821 \r
9822 void\r
9823 SettingsPopUp(ChessProgramState *cps)\r
9824 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9825       EngineOptionsPopup(savedHwnd, cps);\r
9826 }\r
9827 \r
9828 int flock(int fid, int code)\r
9829 {\r
9830     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9831     OVERLAPPED ov;\r
9832     ov.hEvent = NULL;\r
9833     ov.Offset = 0;\r
9834     ov.OffsetHigh = 0;\r
9835     switch(code) {\r
9836       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9837       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9838       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9839       default: return -1;\r
9840     }\r
9841     return 0;\r
9842 }\r