Add -userFileDirectory option
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern int ics_type;\r
104 \r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
106 VOID NewVariantPopup(HWND hwnd);\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
108                    /*char*/int promoChar));\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127   ChessSquare piece;\r
128 } DragInfo;\r
129 \r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
131 \r
132 typedef struct {\r
133   POINT sq[2];    /* board coordinates of from, to squares */\r
134 } HighlightInfo;\r
135 \r
136 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
140 \r
141 typedef struct { // [HGM] atomic\r
142   int fromX, fromY, toX, toY, radius;\r
143 } ExplodeInfo;\r
144 \r
145 static ExplodeInfo explodeInfo;\r
146 \r
147 /* Window class names */\r
148 char szAppName[] = "WinBoard";\r
149 char szConsoleName[] = "WBConsole";\r
150 \r
151 /* Title bar text */\r
152 char szTitle[] = "WinBoard";\r
153 char szConsoleTitle[] = "I C S Interaction";\r
154 \r
155 char *programName;\r
156 char *settingsFileName;\r
157 Boolean saveSettingsOnExit;\r
158 char installDir[MSG_SIZ];\r
159 int errorExitStatus;\r
160 \r
161 BoardSize boardSize;\r
162 Boolean chessProgram;\r
163 //static int boardX, boardY;\r
164 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
165 int squareSize, lineGap, minorSize;\r
166 static int winW, winH;\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
168 static int logoHeight = 0;\r
169 static char messageText[MESSAGE_TEXT_MAX];\r
170 static int clockTimerEvent = 0;\r
171 static int loadGameTimerEvent = 0;\r
172 static int analysisTimerEvent = 0;\r
173 static DelayedEventCallback delayedTimerCallback;\r
174 static int delayedTimerEvent = 0;\r
175 static int buttonCount = 2;\r
176 char *icsTextMenuString;\r
177 char *icsNames;\r
178 char *firstChessProgramNames;\r
179 char *secondChessProgramNames;\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 Boolean alwaysOnTop = FALSE;\r
185 RECT boardRect;\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
187   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
188 HPALETTE hPal;\r
189 ColorClass currentColorClass;\r
190 \r
191 static HWND savedHwnd;\r
192 HWND hCommPort = NULL;    /* currently open comm port */\r
193 static HWND hwndPause;    /* pause button */\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,\r
196   blackSquareBrush, /* [HGM] for band between board and holdings */\r
197   explodeBrush,     /* [HGM] atomic */\r
198   markerBrush,      /* [HGM] markers */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 static HBITMAP liteBackTexture = NULL;\r
214 static HBITMAP darkBackTexture = NULL;\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int backTextureSquareSize = 0;\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
219 \r
220 #if __GNUC__ && !defined(_winmajor)\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
222 #else\r
223 #if defined(_winmajor)\r
224 #define oldDialog (_winmajor < 4)\r
225 #else\r
226 #define oldDialog 0\r
227 #endif\r
228 #endif\r
229 \r
230 #define INTERNATIONAL\r
231 \r
232 #ifdef INTERNATIONAL\r
233 #  define _(s) T_(s)\r
234 #  define N_(s) s\r
235 #else\r
236 #  define _(s) s\r
237 #  define N_(s) s\r
238 #  define T_(s) s\r
239 #  define Translate(x, y)\r
240 #  define LoadLanguageFile(s)\r
241 #endif\r
242 \r
243 #ifdef INTERNATIONAL\r
244 \r
245 Boolean barbaric; // flag indicating if translation is needed\r
246 \r
247 // list of item numbers used in each dialog (used to alter language at run time)\r
248 \r
249 #define ABOUTBOX -1  /* not sure why these are needed */\r
250 #define ABOUTBOX2 -1\r
251 \r
252 int dialogItems[][40] = {\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
255   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
257 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
258   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
259 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
260 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
261   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
262 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
263 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
264   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
265 { ABOUTBOX2, IDC_ChessBoard }, \r
266 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
267   OPT_GameListClose, IDC_GameListDoFilter }, \r
268 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
269 { DLG_Error, IDOK }, \r
270 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
271   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
272 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
273 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
274   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
275   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
276 { DLG_IndexNumber, IDC_Index }, \r
277 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
278 { DLG_TypeInName, IDOK, IDCANCEL }, \r
279 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
280   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
281 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
282   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
283   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
284   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
285   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
286   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
287   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
288 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
289   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
290   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
291   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
292   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
293   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
294   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
295   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
296   GPB_General, GPB_Alarm }, \r
297 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
298   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
299   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
300   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
301   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
302   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
303   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
304   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
305 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
306   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
307   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
308   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
309   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
310   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
311   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
312   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
313   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
314 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
315   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
316   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
317   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
318 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
319 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
320   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
321 { DLG_MoveHistory }, \r
322 { DLG_EvalGraph }, \r
323 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
324 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
325 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
326   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
327   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
328   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
329 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
330   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
331   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
332 { 0 }\r
333 };\r
334 \r
335 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
336 static int lastChecked;\r
337 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
338 extern int tinyLayout;\r
339 extern char * menuBarText[][10];\r
340 \r
341 void\r
342 LoadLanguageFile(char *name)\r
343 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
344     FILE *f;\r
345     int i=0, j=0, n=0, k;\r
346     char buf[MSG_SIZ];\r
347 \r
348     if(!name || name[0] == NULLCHAR) return;\r
349       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
350     appData.language = oldLanguage;\r
351     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
352     if((f = fopen(buf, "r")) == NULL) return;\r
353     while((k = fgetc(f)) != EOF) {\r
354         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
355         languageBuf[i] = k;\r
356         if(k == '\n') {\r
357             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
358                 char *p;\r
359                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
360                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
361                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
362                         english[j] = languageBuf + n + 1; *p = 0;\r
363                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
364 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
365                     }\r
366                 }\r
367             }\r
368             n = i + 1;\r
369         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
370             switch(k) {\r
371               case 'n': k = '\n'; break;\r
372               case 'r': k = '\r'; break;\r
373               case 't': k = '\t'; break;\r
374             }\r
375             languageBuf[--i] = k;\r
376         }\r
377         i++;\r
378     }\r
379     fclose(f);\r
380     barbaric = (j != 0);\r
381     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
382 }\r
383 \r
384 char *\r
385 T_(char *s)\r
386 {   // return the translation of the given string\r
387     // efficiency can be improved a lot...\r
388     int i=0;\r
389 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
390     if(!barbaric) return s;\r
391     if(!s) return ""; // sanity\r
392     while(english[i]) {\r
393         if(!strcmp(s, english[i])) return foreign[i];\r
394         i++;\r
395     }\r
396     return s;\r
397 }\r
398 \r
399 void\r
400 Translate(HWND hDlg, int dialogID)\r
401 {   // translate all text items in the given dialog\r
402     int i=0, j, k;\r
403     char buf[MSG_SIZ], *s;\r
404     if(!barbaric) return;\r
405     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
406     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
407     GetWindowText( hDlg, buf, MSG_SIZ );\r
408     s = T_(buf);\r
409     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
410     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
411         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
412         if(strlen(buf) == 0) continue;\r
413         s = T_(buf);\r
414         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
415     }\r
416 }\r
417 \r
418 HMENU\r
419 TranslateOneMenu(int i, HMENU subMenu)\r
420 {\r
421     int j;\r
422     static MENUITEMINFO info;\r
423 \r
424     info.cbSize = sizeof(MENUITEMINFO);\r
425     info.fMask = MIIM_STATE | MIIM_TYPE;\r
426           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
427             char buf[MSG_SIZ];\r
428             info.dwTypeData = buf;\r
429             info.cch = sizeof(buf);\r
430             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
431             if(i < 10) {
432                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
433                 else menuText[i][j] = strdup(buf); // remember original on first change\r
434             }\r
435             if(buf[0] == NULLCHAR) continue;\r
436             info.dwTypeData = T_(buf);\r
437             info.cch = strlen(buf)+1;\r
438             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
439           }\r
440     return subMenu;\r
441 }\r
442 \r
443 void\r
444 TranslateMenus(int addLanguage)\r
445 {\r
446     int i;\r
447     WIN32_FIND_DATA fileData;\r
448     HANDLE hFind;\r
449 #define IDM_English 1970\r
450     if(1) {\r
451         HMENU mainMenu = GetMenu(hwndMain);\r
452         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
453           HMENU subMenu = GetSubMenu(mainMenu, i);\r
454           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
455                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
456           TranslateOneMenu(i, subMenu);\r
457         }\r
458         DrawMenuBar(hwndMain);\r
459     }\r
460 \r
461     if(!addLanguage) return;\r
462     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
463         HMENU mainMenu = GetMenu(hwndMain);\r
464         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
465         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
466         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
467         i = 0; lastChecked = IDM_English;\r
468         do {\r
469             char *p, *q = fileData.cFileName;\r
470             int checkFlag = MF_UNCHECKED;\r
471             languageFile[i] = strdup(q);\r
472             if(barbaric && !strcmp(oldLanguage, q)) {\r
473                 checkFlag = MF_CHECKED;\r
474                 lastChecked = IDM_English + i + 1;\r
475                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
476             }\r
477             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
478             p = strstr(fileData.cFileName, ".lng");\r
479             if(p) *p = 0;\r
480             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
481         } while(FindNextFile(hFind, &fileData));\r
482         FindClose(hFind);\r
483     }\r
484 }\r
485 \r
486 #endif\r
487 \r
488 typedef struct {\r
489   char *name;\r
490   int squareSize;\r
491   int lineGap;\r
492   int smallLayout;\r
493   int tinyLayout;\r
494   int cliWidth, cliHeight;\r
495 } SizeInfo;\r
496 \r
497 SizeInfo sizeInfo[] = \r
498 {\r
499   { "tiny",     21, 0, 1, 1, 0, 0 },\r
500   { "teeny",    25, 1, 1, 1, 0, 0 },\r
501   { "dinky",    29, 1, 1, 1, 0, 0 },\r
502   { "petite",   33, 1, 1, 1, 0, 0 },\r
503   { "slim",     37, 2, 1, 0, 0, 0 },\r
504   { "small",    40, 2, 1, 0, 0, 0 },\r
505   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
506   { "middling", 49, 2, 0, 0, 0, 0 },\r
507   { "average",  54, 2, 0, 0, 0, 0 },\r
508   { "moderate", 58, 3, 0, 0, 0, 0 },\r
509   { "medium",   64, 3, 0, 0, 0, 0 },\r
510   { "bulky",    72, 3, 0, 0, 0, 0 },\r
511   { "large",    80, 3, 0, 0, 0, 0 },\r
512   { "big",      87, 3, 0, 0, 0, 0 },\r
513   { "huge",     95, 3, 0, 0, 0, 0 },\r
514   { "giant",    108, 3, 0, 0, 0, 0 },\r
515   { "colossal", 116, 4, 0, 0, 0, 0 },\r
516   { "titanic",  129, 4, 0, 0, 0, 0 },\r
517   { NULL, 0, 0, 0, 0, 0, 0 }\r
518 };\r
519 \r
520 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
521 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
522 {\r
523   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
524   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
525   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
526   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
527   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
528   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
529   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
530   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
531   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
532   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
533   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
534   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
535   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
536   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
537   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
538   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
539   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
540   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
541 };\r
542 \r
543 MyFont *font[NUM_SIZES][NUM_FONTS];\r
544 \r
545 typedef struct {\r
546   char *label;\r
547   int id;\r
548   HWND hwnd;\r
549   WNDPROC wndproc;\r
550 } MyButtonDesc;\r
551 \r
552 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
553 #define N_BUTTONS 5\r
554 \r
555 MyButtonDesc buttonDesc[N_BUTTONS] =\r
556 {\r
557   {"<<", IDM_ToStart, NULL, NULL},\r
558   {"<", IDM_Backward, NULL, NULL},\r
559   {"P", IDM_Pause, NULL, NULL},\r
560   {">", IDM_Forward, NULL, NULL},\r
561   {">>", IDM_ToEnd, NULL, NULL},\r
562 };\r
563 \r
564 int tinyLayout = 0, smallLayout = 0;\r
565 #define MENU_BAR_ITEMS 9\r
566 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
567   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
568   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
569 };\r
570 \r
571 \r
572 MySound sounds[(int)NSoundClasses];\r
573 MyTextAttribs textAttribs[(int)NColorClasses];\r
574 \r
575 MyColorizeAttribs colorizeAttribs[] = {\r
576   { (COLORREF)0, 0, N_("Shout Text") },\r
577   { (COLORREF)0, 0, N_("SShout/CShout") },\r
578   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
579   { (COLORREF)0, 0, N_("Channel Text") },\r
580   { (COLORREF)0, 0, N_("Kibitz Text") },\r
581   { (COLORREF)0, 0, N_("Tell Text") },\r
582   { (COLORREF)0, 0, N_("Challenge Text") },\r
583   { (COLORREF)0, 0, N_("Request Text") },\r
584   { (COLORREF)0, 0, N_("Seek Text") },\r
585   { (COLORREF)0, 0, N_("Normal Text") },\r
586   { (COLORREF)0, 0, N_("None") }\r
587 };\r
588 \r
589 \r
590 \r
591 static char *commentTitle;\r
592 static char *commentText;\r
593 static int commentIndex;\r
594 static Boolean editComment = FALSE;\r
595 \r
596 \r
597 char errorTitle[MSG_SIZ];\r
598 char errorMessage[2*MSG_SIZ];\r
599 HWND errorDialog = NULL;\r
600 BOOLEAN moveErrorMessageUp = FALSE;\r
601 BOOLEAN consoleEcho = TRUE;\r
602 CHARFORMAT consoleCF;\r
603 COLORREF consoleBackgroundColor;\r
604 \r
605 char *programVersion;\r
606 \r
607 #define CPReal 1\r
608 #define CPComm 2\r
609 #define CPSock 3\r
610 #define CPRcmd 4\r
611 typedef int CPKind;\r
612 \r
613 typedef struct {\r
614   CPKind kind;\r
615   HANDLE hProcess;\r
616   DWORD pid;\r
617   HANDLE hTo;\r
618   HANDLE hFrom;\r
619   SOCKET sock;\r
620   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
621 } ChildProc;\r
622 \r
623 #define INPUT_SOURCE_BUF_SIZE 4096\r
624 \r
625 typedef struct _InputSource {\r
626   CPKind kind;\r
627   HANDLE hFile;\r
628   SOCKET sock;\r
629   int lineByLine;\r
630   HANDLE hThread;\r
631   DWORD id;\r
632   char buf[INPUT_SOURCE_BUF_SIZE];\r
633   char *next;\r
634   DWORD count;\r
635   int error;\r
636   InputCallback func;\r
637   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
638   VOIDSTAR closure;\r
639 } InputSource;\r
640 \r
641 InputSource *consoleInputSource;\r
642 \r
643 DCB dcb;\r
644 \r
645 /* forward */\r
646 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
647 VOID ConsoleCreate();\r
648 LRESULT CALLBACK\r
649   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
650 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
651 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
652 VOID ParseCommSettings(char *arg, DCB *dcb);\r
653 LRESULT CALLBACK\r
654   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
655 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
656 void ParseIcsTextMenu(char *icsTextMenuString);\r
657 VOID PopUpNameDialog(char firstchar);\r
658 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
659 \r
660 /* [AS] */\r
661 int NewGameFRC();\r
662 int GameListOptions();\r
663 \r
664 int dummy; // [HGM] for obsolete args\r
665 \r
666 HWND hwndMain = NULL;        /* root window*/\r
667 HWND hwndConsole = NULL;\r
668 HWND commentDialog = NULL;\r
669 HWND moveHistoryDialog = NULL;\r
670 HWND evalGraphDialog = NULL;\r
671 HWND engineOutputDialog = NULL;\r
672 HWND gameListDialog = NULL;\r
673 HWND editTagsDialog = NULL;\r
674 \r
675 int commentUp = FALSE;\r
676 \r
677 WindowPlacement wpMain;\r
678 WindowPlacement wpConsole;\r
679 WindowPlacement wpComment;\r
680 WindowPlacement wpMoveHistory;\r
681 WindowPlacement wpEvalGraph;\r
682 WindowPlacement wpEngineOutput;\r
683 WindowPlacement wpGameList;\r
684 WindowPlacement wpTags;\r
685 \r
686 VOID EngineOptionsPopup(); // [HGM] settings\r
687 \r
688 VOID GothicPopUp(char *title, VariantClass variant);\r
689 /*\r
690  * Setting "frozen" should disable all user input other than deleting\r
691  * the window.  We do this while engines are initializing themselves.\r
692  */\r
693 static int frozen = 0;\r
694 static int oldMenuItemState[MENU_BAR_ITEMS];\r
695 void FreezeUI()\r
696 {\r
697   HMENU hmenu;\r
698   int i;\r
699 \r
700   if (frozen) return;\r
701   frozen = 1;\r
702   hmenu = GetMenu(hwndMain);\r
703   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
704     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
705   }\r
706   DrawMenuBar(hwndMain);\r
707 }\r
708 \r
709 /* Undo a FreezeUI */\r
710 void ThawUI()\r
711 {\r
712   HMENU hmenu;\r
713   int i;\r
714 \r
715   if (!frozen) return;\r
716   frozen = 0;\r
717   hmenu = GetMenu(hwndMain);\r
718   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
719     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
720   }\r
721   DrawMenuBar(hwndMain);\r
722 }\r
723 \r
724 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
725 \r
726 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
727 #ifdef JAWS\r
728 #include "jaws.c"\r
729 #else\r
730 #define JAWS_INIT\r
731 #define JAWS_ARGS\r
732 #define JAWS_ALT_INTERCEPT\r
733 #define JAWS_KB_NAVIGATION\r
734 #define JAWS_MENU_ITEMS\r
735 #define JAWS_SILENCE\r
736 #define JAWS_REPLAY\r
737 #define JAWS_ACCEL\r
738 #define JAWS_COPYRIGHT\r
739 #define JAWS_DELETE(X) X\r
740 #define SAYMACHINEMOVE()\r
741 #define SAY(X)\r
742 #endif\r
743 \r
744 /*---------------------------------------------------------------------------*\\r
745  *\r
746  * WinMain\r
747  *\r
748 \*---------------------------------------------------------------------------*/\r
749 \r
750 int APIENTRY\r
751 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
752         LPSTR lpCmdLine, int nCmdShow)\r
753 {\r
754   MSG msg;\r
755   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
756 //  INITCOMMONCONTROLSEX ex;\r
757 \r
758   debugFP = stderr;\r
759 \r
760   LoadLibrary("RICHED32.DLL");\r
761   consoleCF.cbSize = sizeof(CHARFORMAT);\r
762 \r
763   if (!InitApplication(hInstance)) {\r
764     return (FALSE);\r
765   }\r
766   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
767     return (FALSE);\r
768   }\r
769 \r
770   JAWS_INIT\r
771   TranslateMenus(1);\r
772 \r
773 //  InitCommonControlsEx(&ex);\r
774   InitCommonControls();\r
775 \r
776   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
777   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
778   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
779 \r
780   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
781 \r
782   while (GetMessage(&msg, /* message structure */\r
783                     NULL, /* handle of window receiving the message */\r
784                     0,    /* lowest message to examine */\r
785                     0))   /* highest message to examine */\r
786     {\r
787 \r
788       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
789         // [HGM] navigate: switch between all windows with tab\r
790         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
791         int i, currentElement = 0;\r
792 \r
793         // first determine what element of the chain we come from (if any)\r
794         if(appData.icsActive) {\r
795             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
796             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
797         }\r
798         if(engineOutputDialog && EngineOutputIsUp()) {\r
799             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
800             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
801         }\r
802         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
803             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
804         }\r
805         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
806         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
807         if(msg.hwnd == e1)                 currentElement = 2; else\r
808         if(msg.hwnd == e2)                 currentElement = 3; else\r
809         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
810         if(msg.hwnd == mh)                currentElement = 4; else\r
811         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
812         if(msg.hwnd == hText)  currentElement = 5; else\r
813         if(msg.hwnd == hInput) currentElement = 6; else\r
814         for (i = 0; i < N_BUTTONS; i++) {\r
815             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
816         }\r
817 \r
818         // determine where to go to\r
819         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
820           do {\r
821             currentElement = (currentElement + direction) % 7;\r
822             switch(currentElement) {\r
823                 case 0:\r
824                   h = hwndMain; break; // passing this case always makes the loop exit\r
825                 case 1:\r
826                   h = buttonDesc[0].hwnd; break; // could be NULL\r
827                 case 2:\r
828                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
829                   h = e1; break;\r
830                 case 3:\r
831                   if(!EngineOutputIsUp()) continue;\r
832                   h = e2; break;\r
833                 case 4:\r
834                   if(!MoveHistoryIsUp()) continue;\r
835                   h = mh; break;\r
836 //              case 6: // input to eval graph does not seem to get here!\r
837 //                if(!EvalGraphIsUp()) continue;\r
838 //                h = evalGraphDialog; break;\r
839                 case 5:\r
840                   if(!appData.icsActive) continue;\r
841                   SAY("display");\r
842                   h = hText; break;\r
843                 case 6:\r
844                   if(!appData.icsActive) continue;\r
845                   SAY("input");\r
846                   h = hInput; break;\r
847             }\r
848           } while(h == 0);\r
849 \r
850           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
851           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
852           SetFocus(h);\r
853 \r
854           continue; // this message now has been processed\r
855         }\r
856       }\r
857 \r
858       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
859           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
860           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
861           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
862           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
863           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
864           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
865           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
866           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
867           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
868         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
869         for(i=0; i<MAX_CHAT; i++) \r
870             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
871                 done = 1; break;\r
872         }\r
873         if(done) continue; // [HGM] chat: end patch\r
874         TranslateMessage(&msg); /* Translates virtual key codes */\r
875         DispatchMessage(&msg);  /* Dispatches message to window */\r
876       }\r
877     }\r
878 \r
879 \r
880   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
881 }\r
882 \r
883 /*---------------------------------------------------------------------------*\\r
884  *\r
885  * Initialization functions\r
886  *\r
887 \*---------------------------------------------------------------------------*/\r
888 \r
889 void\r
890 SetUserLogo()\r
891 {   // update user logo if necessary\r
892     static char oldUserName[MSG_SIZ], *curName;\r
893 \r
894     if(appData.autoLogo) {\r
895           curName = UserName();\r
896           if(strcmp(curName, oldUserName)) {\r
897             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
898                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
899                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
900                 if(userLogo == NULL)\r
901                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
902           }\r
903     }\r
904 }\r
905 \r
906 BOOL\r
907 InitApplication(HINSTANCE hInstance)\r
908 {\r
909   WNDCLASS wc;\r
910 \r
911   /* Fill in window class structure with parameters that describe the */\r
912   /* main window. */\r
913 \r
914   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
915   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
916   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
917   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
918   wc.hInstance     = hInstance;         /* Owner of this class */\r
919   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
920   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
921   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
922   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
923   wc.lpszClassName = szAppName;                 /* Name to register as */\r
924 \r
925   /* Register the window class and return success/failure code. */\r
926   if (!RegisterClass(&wc)) return FALSE;\r
927 \r
928   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
929   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
930   wc.cbClsExtra    = 0;\r
931   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
932   wc.hInstance     = hInstance;\r
933   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
934   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
935   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
936   wc.lpszMenuName  = NULL;\r
937   wc.lpszClassName = szConsoleName;\r
938 \r
939   if (!RegisterClass(&wc)) return FALSE;\r
940   return TRUE;\r
941 }\r
942 \r
943 \r
944 /* Set by InitInstance, used by EnsureOnScreen */\r
945 int screenHeight, screenWidth;\r
946 \r
947 void\r
948 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
949 {\r
950 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
951   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
952   if (*x > screenWidth - 32) *x = 0;\r
953   if (*y > screenHeight - 32) *y = 0;\r
954   if (*x < minX) *x = minX;\r
955   if (*y < minY) *y = minY;\r
956 }\r
957 \r
958 VOID\r
959 LoadLogo(ChessProgramState *cps, int n)\r
960 {\r
961   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
962       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
963 \r
964       if (cps->programLogo == NULL && appData.debugMode) {\r
965           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
966       }\r
967   } else if(appData.autoLogo) {\r
968       if(appData.firstDirectory && appData.directory[n][0]) {\r
969         char buf[MSG_SIZ];\r
970           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.directory[n]);\r
971         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
972       }\r
973   }\r
974 }\r
975 \r
976 BOOL\r
977 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
978 {\r
979   HWND hwnd; /* Main window handle. */\r
980   int ibs;\r
981   WINDOWPLACEMENT wp;\r
982   char *filepart;\r
983 \r
984   hInst = hInstance;    /* Store instance handle in our global variable */\r
985   programName = szAppName;\r
986 \r
987   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
988     *filepart = NULLCHAR;\r
989   } else {\r
990     GetCurrentDirectory(MSG_SIZ, installDir);\r
991   }\r
992   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
993   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
994   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
995   /* xboard, and older WinBoards, controlled the move sound with the\r
996      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
997      always turn the option on (so that the backend will call us),\r
998      then let the user turn the sound off by setting it to silence if\r
999      desired.  To accommodate old winboard.ini files saved by old\r
1000      versions of WinBoard, we also turn off the sound if the option\r
1001      was initially set to false. [HGM] taken out of InitAppData */\r
1002   if (!appData.ringBellAfterMoves) {\r
1003     sounds[(int)SoundMove].name = strdup("");\r
1004     appData.ringBellAfterMoves = TRUE;\r
1005   }\r
1006   if (appData.debugMode) {\r
1007     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1008     setbuf(debugFP, NULL);\r
1009   }\r
1010 \r
1011   LoadLanguageFile(appData.language);\r
1012 \r
1013   InitBackEnd1();\r
1014 \r
1015 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1016 //  InitEngineUCI( installDir, &second );\r
1017 \r
1018   /* Create a main window for this application instance. */\r
1019   hwnd = CreateWindow(szAppName, szTitle,\r
1020                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1021                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1022                       NULL, NULL, hInstance, NULL);\r
1023   hwndMain = hwnd;\r
1024 \r
1025   /* If window could not be created, return "failure" */\r
1026   if (!hwnd) {\r
1027     return (FALSE);\r
1028   }\r
1029 \r
1030   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1031   LoadLogo(&first, 0);\r
1032 \r
1033   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1034       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1035 \r
1036       if (second.programLogo == NULL && appData.debugMode) {\r
1037           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1038       }\r
1039   } else if(appData.autoLogo) {\r
1040       char buf[MSG_SIZ];\r
1041       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1042         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1043         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1044       } else\r
1045       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1046         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1047         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1048       }\r
1049   }\r
1050 \r
1051   SetUserLogo();\r
1052 \r
1053   iconWhite = LoadIcon(hInstance, "icon_white");\r
1054   iconBlack = LoadIcon(hInstance, "icon_black");\r
1055   iconCurrent = iconWhite;\r
1056   InitDrawingColors();\r
1057   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1058   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1059   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1060     /* Compute window size for each board size, and use the largest\r
1061        size that fits on this screen as the default. */\r
1062     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1063     if (boardSize == (BoardSize)-1 &&\r
1064         winH <= screenHeight\r
1065            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1066         && winW <= screenWidth) {\r
1067       boardSize = (BoardSize)ibs;\r
1068     }\r
1069   }\r
1070 \r
1071   InitDrawingSizes(boardSize, 0);\r
1072   InitMenuChecks();\r
1073   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1074 \r
1075   /* [AS] Load textures if specified */\r
1076   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1077   \r
1078   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1079       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1080       liteBackTextureMode = appData.liteBackTextureMode;\r
1081 \r
1082       if (liteBackTexture == NULL && appData.debugMode) {\r
1083           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1084       }\r
1085   }\r
1086   \r
1087   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1088       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1089       darkBackTextureMode = appData.darkBackTextureMode;\r
1090 \r
1091       if (darkBackTexture == NULL && appData.debugMode) {\r
1092           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1093       }\r
1094   }\r
1095 \r
1096   mysrandom( (unsigned) time(NULL) );\r
1097 \r
1098   /* [AS] Restore layout */\r
1099   if( wpMoveHistory.visible ) {\r
1100       MoveHistoryPopUp();\r
1101   }\r
1102 \r
1103   if( wpEvalGraph.visible ) {\r
1104       EvalGraphPopUp();\r
1105   }\r
1106 \r
1107   if( wpEngineOutput.visible ) {\r
1108       EngineOutputPopUp();\r
1109   }\r
1110 \r
1111   /* Make the window visible; update its client area; and return "success" */\r
1112   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1113   wp.length = sizeof(WINDOWPLACEMENT);\r
1114   wp.flags = 0;\r
1115   wp.showCmd = nCmdShow;\r
1116   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1117   wp.rcNormalPosition.left = wpMain.x;\r
1118   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1119   wp.rcNormalPosition.top = wpMain.y;\r
1120   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1121   SetWindowPlacement(hwndMain, &wp);\r
1122 \r
1123   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1124 \r
1125   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1126                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1127 \r
1128   if (hwndConsole) {\r
1129 #if AOT_CONSOLE\r
1130     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1131                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1132 #endif\r
1133     ShowWindow(hwndConsole, nCmdShow);\r
1134     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1135       char buf[MSG_SIZ], *p = buf, *q;\r
1136         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1137       do {\r
1138         q = strchr(p, ';');\r
1139         if(q) *q++ = 0;\r
1140         if(*p) ChatPopUp(p);\r
1141       } while(p=q);\r
1142     }\r
1143     SetActiveWindow(hwndConsole);\r
1144   }\r
1145   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1146   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1147 \r
1148   return TRUE;\r
1149 \r
1150 }\r
1151 \r
1152 VOID\r
1153 InitMenuChecks()\r
1154 {\r
1155   HMENU hmenu = GetMenu(hwndMain);\r
1156 \r
1157   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1158                         MF_BYCOMMAND|((appData.icsActive &&\r
1159                                        *appData.icsCommPort != NULLCHAR) ?\r
1160                                       MF_ENABLED : MF_GRAYED));\r
1161   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1162                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1163                                      MF_CHECKED : MF_UNCHECKED));\r
1164 }\r
1165 \r
1166 //---------------------------------------------------------------------------------------------------------\r
1167 \r
1168 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1169 #define XBOARD FALSE\r
1170 \r
1171 #define OPTCHAR "/"\r
1172 #define SEPCHAR "="\r
1173 \r
1174 #include "args.h"\r
1175 \r
1176 // front-end part of option handling\r
1177 \r
1178 VOID\r
1179 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1180 {\r
1181   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1182   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1183   DeleteDC(hdc);\r
1184   lf->lfWidth = 0;\r
1185   lf->lfEscapement = 0;\r
1186   lf->lfOrientation = 0;\r
1187   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1188   lf->lfItalic = mfp->italic;\r
1189   lf->lfUnderline = mfp->underline;\r
1190   lf->lfStrikeOut = mfp->strikeout;\r
1191   lf->lfCharSet = mfp->charset;\r
1192   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1193   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1194   lf->lfQuality = DEFAULT_QUALITY;\r
1195   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1196     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1197 }\r
1198 \r
1199 void\r
1200 CreateFontInMF(MyFont *mf)\r
1201\r
1202   LFfromMFP(&mf->lf, &mf->mfp);\r
1203   if (mf->hf) DeleteObject(mf->hf);\r
1204   mf->hf = CreateFontIndirect(&mf->lf);\r
1205 }\r
1206 \r
1207 // [HGM] This platform-dependent table provides the location for storing the color info\r
1208 void *\r
1209 colorVariable[] = {\r
1210   &whitePieceColor, \r
1211   &blackPieceColor, \r
1212   &lightSquareColor,\r
1213   &darkSquareColor, \r
1214   &highlightSquareColor,\r
1215   &premoveHighlightColor,\r
1216   NULL,\r
1217   &consoleBackgroundColor,\r
1218   &appData.fontForeColorWhite,\r
1219   &appData.fontBackColorWhite,\r
1220   &appData.fontForeColorBlack,\r
1221   &appData.fontBackColorBlack,\r
1222   &appData.evalHistColorWhite,\r
1223   &appData.evalHistColorBlack,\r
1224   &appData.highlightArrowColor,\r
1225 };\r
1226 \r
1227 /* Command line font name parser.  NULL name means do nothing.\r
1228    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1229    For backward compatibility, syntax without the colon is also\r
1230    accepted, but font names with digits in them won't work in that case.\r
1231 */\r
1232 VOID\r
1233 ParseFontName(char *name, MyFontParams *mfp)\r
1234 {\r
1235   char *p, *q;\r
1236   if (name == NULL) return;\r
1237   p = name;\r
1238   q = strchr(p, ':');\r
1239   if (q) {\r
1240     if (q - p >= sizeof(mfp->faceName))\r
1241       ExitArgError(_("Font name too long:"), name);\r
1242     memcpy(mfp->faceName, p, q - p);\r
1243     mfp->faceName[q - p] = NULLCHAR;\r
1244     p = q + 1;\r
1245   } else {\r
1246     q = mfp->faceName;\r
1247     while (*p && !isdigit(*p)) {\r
1248       *q++ = *p++;\r
1249       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1250         ExitArgError(_("Font name too long:"), name);\r
1251     }\r
1252     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1253     *q = NULLCHAR;\r
1254   }\r
1255   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1256   mfp->pointSize = (float) atof(p);\r
1257   mfp->bold = (strchr(p, 'b') != NULL);\r
1258   mfp->italic = (strchr(p, 'i') != NULL);\r
1259   mfp->underline = (strchr(p, 'u') != NULL);\r
1260   mfp->strikeout = (strchr(p, 's') != NULL);\r
1261   mfp->charset = DEFAULT_CHARSET;\r
1262   q = strchr(p, 'c');\r
1263   if (q)\r
1264     mfp->charset = (BYTE) atoi(q+1);\r
1265 }\r
1266 \r
1267 void\r
1268 ParseFont(char *name, int number)\r
1269 { // wrapper to shield back-end from 'font'\r
1270   ParseFontName(name, &font[boardSize][number]->mfp);\r
1271 }\r
1272 \r
1273 void\r
1274 SetFontDefaults()\r
1275 { // in WB  we have a 2D array of fonts; this initializes their description\r
1276   int i, j;\r
1277   /* Point font array elements to structures and\r
1278      parse default font names */\r
1279   for (i=0; i<NUM_FONTS; i++) {\r
1280     for (j=0; j<NUM_SIZES; j++) {\r
1281       font[j][i] = &fontRec[j][i];\r
1282       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1283     }\r
1284   }\r
1285 }\r
1286 \r
1287 void\r
1288 CreateFonts()\r
1289 { // here we create the actual fonts from the selected descriptions\r
1290   int i, j;\r
1291   for (i=0; i<NUM_FONTS; i++) {\r
1292     for (j=0; j<NUM_SIZES; j++) {\r
1293       CreateFontInMF(font[j][i]);\r
1294     }\r
1295   }\r
1296 }\r
1297 /* Color name parser.\r
1298    X version accepts X color names, but this one\r
1299    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1300 COLORREF\r
1301 ParseColorName(char *name)\r
1302 {\r
1303   int red, green, blue, count;\r
1304   char buf[MSG_SIZ];\r
1305 \r
1306   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1307   if (count != 3) {\r
1308     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1309       &red, &green, &blue);\r
1310   }\r
1311   if (count != 3) {\r
1312     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1313     DisplayError(buf, 0);\r
1314     return RGB(0, 0, 0);\r
1315   }\r
1316   return PALETTERGB(red, green, blue);\r
1317 }\r
1318 \r
1319 void\r
1320 ParseColor(int n, char *name)\r
1321 { // for WinBoard the color is an int, which needs to be derived from the string\r
1322   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1323 }\r
1324 \r
1325 void\r
1326 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1327 {\r
1328   char *e = argValue;\r
1329   int eff = 0;\r
1330 \r
1331   while (*e) {\r
1332     if (*e == 'b')      eff |= CFE_BOLD;\r
1333     else if (*e == 'i') eff |= CFE_ITALIC;\r
1334     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1335     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1336     else if (*e == '#' || isdigit(*e)) break;\r
1337     e++;\r
1338   }\r
1339   *effects = eff;\r
1340   *color   = ParseColorName(e);\r
1341 }\r
1342 \r
1343 void\r
1344 ParseTextAttribs(ColorClass cc, char *s)\r
1345 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1346     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1347     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1348 }\r
1349 \r
1350 void\r
1351 ParseBoardSize(void *addr, char *name)\r
1352 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1353   BoardSize bs = SizeTiny;\r
1354   while (sizeInfo[bs].name != NULL) {\r
1355     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1356         *(BoardSize *)addr = bs;\r
1357         return;\r
1358     }\r
1359     bs++;\r
1360   }\r
1361   ExitArgError(_("Unrecognized board size value"), name);\r
1362 }\r
1363 \r
1364 void\r
1365 LoadAllSounds()\r
1366 { // [HGM] import name from appData first\r
1367   ColorClass cc;\r
1368   SoundClass sc;\r
1369   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1370     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1371     textAttribs[cc].sound.data = NULL;\r
1372     MyLoadSound(&textAttribs[cc].sound);\r
1373   }\r
1374   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1375     textAttribs[cc].sound.name = strdup("");\r
1376     textAttribs[cc].sound.data = NULL;\r
1377   }\r
1378   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1379     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1380     sounds[sc].data = NULL;\r
1381     MyLoadSound(&sounds[sc]);\r
1382   }\r
1383 }\r
1384 \r
1385 void\r
1386 SetCommPortDefaults()\r
1387 {\r
1388    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1389   dcb.DCBlength = sizeof(DCB);\r
1390   dcb.BaudRate = 9600;\r
1391   dcb.fBinary = TRUE;\r
1392   dcb.fParity = FALSE;\r
1393   dcb.fOutxCtsFlow = FALSE;\r
1394   dcb.fOutxDsrFlow = FALSE;\r
1395   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1396   dcb.fDsrSensitivity = FALSE;\r
1397   dcb.fTXContinueOnXoff = TRUE;\r
1398   dcb.fOutX = FALSE;\r
1399   dcb.fInX = FALSE;\r
1400   dcb.fNull = FALSE;\r
1401   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1402   dcb.fAbortOnError = FALSE;\r
1403   dcb.ByteSize = 7;\r
1404   dcb.Parity = SPACEPARITY;\r
1405   dcb.StopBits = ONESTOPBIT;\r
1406 }\r
1407 \r
1408 // [HGM] args: these three cases taken out to stay in front-end\r
1409 void\r
1410 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1411 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1412         // while the curent board size determines the element. This system should be ported to XBoard.\r
1413         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1414         int bs;\r
1415         for (bs=0; bs<NUM_SIZES; bs++) {\r
1416           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1417           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1418           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1419             ad->argName, mfp->faceName, mfp->pointSize,\r
1420             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1421             mfp->bold ? "b" : "",\r
1422             mfp->italic ? "i" : "",\r
1423             mfp->underline ? "u" : "",\r
1424             mfp->strikeout ? "s" : "",\r
1425             (int)mfp->charset);\r
1426         }\r
1427       }\r
1428 \r
1429 void\r
1430 ExportSounds()\r
1431 { // [HGM] copy the names from the internal WB variables to appData\r
1432   ColorClass cc;\r
1433   SoundClass sc;\r
1434   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1435     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1436   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1437     (&appData.soundMove)[sc] = sounds[sc].name;\r
1438 }\r
1439 \r
1440 void\r
1441 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1442 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1443         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1444         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1445           (ta->effects & CFE_BOLD) ? "b" : "",\r
1446           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1447           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1448           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1449           (ta->effects) ? " " : "",\r
1450           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1451       }\r
1452 \r
1453 void\r
1454 SaveColor(FILE *f, ArgDescriptor *ad)\r
1455 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1456         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1457         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1458           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1459 }\r
1460 \r
1461 void\r
1462 SaveBoardSize(FILE *f, char *name, void *addr)\r
1463 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1464   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1465 }\r
1466 \r
1467 void\r
1468 ParseCommPortSettings(char *s)\r
1469 { // wrapper to keep dcb from back-end\r
1470   ParseCommSettings(s, &dcb);\r
1471 }\r
1472 \r
1473 void\r
1474 GetWindowCoords()\r
1475 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1476   GetActualPlacement(hwndMain, &wpMain);\r
1477   GetActualPlacement(hwndConsole, &wpConsole);\r
1478   GetActualPlacement(commentDialog, &wpComment);\r
1479   GetActualPlacement(editTagsDialog, &wpTags);\r
1480   GetActualPlacement(gameListDialog, &wpGameList);\r
1481   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1482   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1483   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1484 }\r
1485 \r
1486 void\r
1487 PrintCommPortSettings(FILE *f, char *name)\r
1488 { // wrapper to shield back-end from DCB\r
1489       PrintCommSettings(f, name, &dcb);\r
1490 }\r
1491 \r
1492 int\r
1493 MySearchPath(char *installDir, char *name, char *fullname)\r
1494 {\r
1495   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1496   if(name[0]== '%') {\r
1497     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1498     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1499       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1500       *strchr(buf, '%') = 0;\r
1501       strcat(fullname, getenv(buf));\r
1502       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1503     }\r
1504     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1505     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1506     return (int) strlen(fullname);\r
1507   }\r
1508   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1509 }\r
1510 \r
1511 int\r
1512 MyGetFullPathName(char *name, char *fullname)\r
1513 {\r
1514   char *dummy;\r
1515   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1516 }\r
1517 \r
1518 int\r
1519 MainWindowUp()\r
1520 { // [HGM] args: allows testing if main window is realized from back-end\r
1521   return hwndMain != NULL;\r
1522 }\r
1523 \r
1524 void\r
1525 PopUpStartupDialog()\r
1526 {\r
1527     FARPROC lpProc;\r
1528     \r
1529     LoadLanguageFile(appData.language);\r
1530     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1531     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1532     FreeProcInstance(lpProc);\r
1533 }\r
1534 \r
1535 /*---------------------------------------------------------------------------*\\r
1536  *\r
1537  * GDI board drawing routines\r
1538  *\r
1539 \*---------------------------------------------------------------------------*/\r
1540 \r
1541 /* [AS] Draw square using background texture */\r
1542 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1543 {\r
1544     XFORM   x;\r
1545 \r
1546     if( mode == 0 ) {\r
1547         return; /* Should never happen! */\r
1548     }\r
1549 \r
1550     SetGraphicsMode( dst, GM_ADVANCED );\r
1551 \r
1552     switch( mode ) {\r
1553     case 1:\r
1554         /* Identity */\r
1555         break;\r
1556     case 2:\r
1557         /* X reflection */\r
1558         x.eM11 = -1.0;\r
1559         x.eM12 = 0;\r
1560         x.eM21 = 0;\r
1561         x.eM22 = 1.0;\r
1562         x.eDx = (FLOAT) dw + dx - 1;\r
1563         x.eDy = 0;\r
1564         dx = 0;\r
1565         SetWorldTransform( dst, &x );\r
1566         break;\r
1567     case 3:\r
1568         /* Y reflection */\r
1569         x.eM11 = 1.0;\r
1570         x.eM12 = 0;\r
1571         x.eM21 = 0;\r
1572         x.eM22 = -1.0;\r
1573         x.eDx = 0;\r
1574         x.eDy = (FLOAT) dh + dy - 1;\r
1575         dy = 0;\r
1576         SetWorldTransform( dst, &x );\r
1577         break;\r
1578     case 4:\r
1579         /* X/Y flip */\r
1580         x.eM11 = 0;\r
1581         x.eM12 = 1.0;\r
1582         x.eM21 = 1.0;\r
1583         x.eM22 = 0;\r
1584         x.eDx = (FLOAT) dx;\r
1585         x.eDy = (FLOAT) dy;\r
1586         dx = 0;\r
1587         dy = 0;\r
1588         SetWorldTransform( dst, &x );\r
1589         break;\r
1590     }\r
1591 \r
1592     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1593 \r
1594     x.eM11 = 1.0;\r
1595     x.eM12 = 0;\r
1596     x.eM21 = 0;\r
1597     x.eM22 = 1.0;\r
1598     x.eDx = 0;\r
1599     x.eDy = 0;\r
1600     SetWorldTransform( dst, &x );\r
1601 \r
1602     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1603 }\r
1604 \r
1605 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1606 enum {\r
1607     PM_WP = (int) WhitePawn, \r
1608     PM_WN = (int) WhiteKnight, \r
1609     PM_WB = (int) WhiteBishop, \r
1610     PM_WR = (int) WhiteRook, \r
1611     PM_WQ = (int) WhiteQueen, \r
1612     PM_WF = (int) WhiteFerz, \r
1613     PM_WW = (int) WhiteWazir, \r
1614     PM_WE = (int) WhiteAlfil, \r
1615     PM_WM = (int) WhiteMan, \r
1616     PM_WO = (int) WhiteCannon, \r
1617     PM_WU = (int) WhiteUnicorn, \r
1618     PM_WH = (int) WhiteNightrider, \r
1619     PM_WA = (int) WhiteAngel, \r
1620     PM_WC = (int) WhiteMarshall, \r
1621     PM_WAB = (int) WhiteCardinal, \r
1622     PM_WD = (int) WhiteDragon, \r
1623     PM_WL = (int) WhiteLance, \r
1624     PM_WS = (int) WhiteCobra, \r
1625     PM_WV = (int) WhiteFalcon, \r
1626     PM_WSG = (int) WhiteSilver, \r
1627     PM_WG = (int) WhiteGrasshopper, \r
1628     PM_WK = (int) WhiteKing,\r
1629     PM_BP = (int) BlackPawn, \r
1630     PM_BN = (int) BlackKnight, \r
1631     PM_BB = (int) BlackBishop, \r
1632     PM_BR = (int) BlackRook, \r
1633     PM_BQ = (int) BlackQueen, \r
1634     PM_BF = (int) BlackFerz, \r
1635     PM_BW = (int) BlackWazir, \r
1636     PM_BE = (int) BlackAlfil, \r
1637     PM_BM = (int) BlackMan,\r
1638     PM_BO = (int) BlackCannon, \r
1639     PM_BU = (int) BlackUnicorn, \r
1640     PM_BH = (int) BlackNightrider, \r
1641     PM_BA = (int) BlackAngel, \r
1642     PM_BC = (int) BlackMarshall, \r
1643     PM_BG = (int) BlackGrasshopper, \r
1644     PM_BAB = (int) BlackCardinal,\r
1645     PM_BD = (int) BlackDragon,\r
1646     PM_BL = (int) BlackLance,\r
1647     PM_BS = (int) BlackCobra,\r
1648     PM_BV = (int) BlackFalcon,\r
1649     PM_BSG = (int) BlackSilver,\r
1650     PM_BK = (int) BlackKing\r
1651 };\r
1652 \r
1653 static HFONT hPieceFont = NULL;\r
1654 static HBITMAP hPieceMask[(int) EmptySquare];\r
1655 static HBITMAP hPieceFace[(int) EmptySquare];\r
1656 static int fontBitmapSquareSize = 0;\r
1657 static char pieceToFontChar[(int) EmptySquare] =\r
1658                               { 'p', 'n', 'b', 'r', 'q', \r
1659                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1660                       'k', 'o', 'm', 'v', 't', 'w', \r
1661                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1662                                                               'l' };\r
1663 \r
1664 extern BOOL SetCharTable( char *table, const char * map );\r
1665 /* [HGM] moved to backend.c */\r
1666 \r
1667 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1668 {\r
1669     HBRUSH hbrush;\r
1670     BYTE r1 = GetRValue( color );\r
1671     BYTE g1 = GetGValue( color );\r
1672     BYTE b1 = GetBValue( color );\r
1673     BYTE r2 = r1 / 2;\r
1674     BYTE g2 = g1 / 2;\r
1675     BYTE b2 = b1 / 2;\r
1676     RECT rc;\r
1677 \r
1678     /* Create a uniform background first */\r
1679     hbrush = CreateSolidBrush( color );\r
1680     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1681     FillRect( hdc, &rc, hbrush );\r
1682     DeleteObject( hbrush );\r
1683     \r
1684     if( mode == 1 ) {\r
1685         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1686         int steps = squareSize / 2;\r
1687         int i;\r
1688 \r
1689         for( i=0; i<steps; i++ ) {\r
1690             BYTE r = r1 - (r1-r2) * i / steps;\r
1691             BYTE g = g1 - (g1-g2) * i / steps;\r
1692             BYTE b = b1 - (b1-b2) * i / steps;\r
1693 \r
1694             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1695             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1696             FillRect( hdc, &rc, hbrush );\r
1697             DeleteObject(hbrush);\r
1698         }\r
1699     }\r
1700     else if( mode == 2 ) {\r
1701         /* Diagonal gradient, good more or less for every piece */\r
1702         POINT triangle[3];\r
1703         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1704         HBRUSH hbrush_old;\r
1705         int steps = squareSize;\r
1706         int i;\r
1707 \r
1708         triangle[0].x = squareSize - steps;\r
1709         triangle[0].y = squareSize;\r
1710         triangle[1].x = squareSize;\r
1711         triangle[1].y = squareSize;\r
1712         triangle[2].x = squareSize;\r
1713         triangle[2].y = squareSize - steps;\r
1714 \r
1715         for( i=0; i<steps; i++ ) {\r
1716             BYTE r = r1 - (r1-r2) * i / steps;\r
1717             BYTE g = g1 - (g1-g2) * i / steps;\r
1718             BYTE b = b1 - (b1-b2) * i / steps;\r
1719 \r
1720             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1721             hbrush_old = SelectObject( hdc, hbrush );\r
1722             Polygon( hdc, triangle, 3 );\r
1723             SelectObject( hdc, hbrush_old );\r
1724             DeleteObject(hbrush);\r
1725             triangle[0].x++;\r
1726             triangle[2].y++;\r
1727         }\r
1728 \r
1729         SelectObject( hdc, hpen );\r
1730     }\r
1731 }\r
1732 \r
1733 /*\r
1734     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1735     seems to work ok. The main problem here is to find the "inside" of a chess\r
1736     piece: follow the steps as explained below.\r
1737 */\r
1738 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1739 {\r
1740     HBITMAP hbm;\r
1741     HBITMAP hbm_old;\r
1742     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1743     RECT rc;\r
1744     SIZE sz;\r
1745     POINT pt;\r
1746     int backColor = whitePieceColor; \r
1747     int foreColor = blackPieceColor;\r
1748     \r
1749     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1750         backColor = appData.fontBackColorWhite;\r
1751         foreColor = appData.fontForeColorWhite;\r
1752     }\r
1753     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1754         backColor = appData.fontBackColorBlack;\r
1755         foreColor = appData.fontForeColorBlack;\r
1756     }\r
1757 \r
1758     /* Mask */\r
1759     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1760 \r
1761     hbm_old = SelectObject( hdc, hbm );\r
1762 \r
1763     rc.left = 0;\r
1764     rc.top = 0;\r
1765     rc.right = squareSize;\r
1766     rc.bottom = squareSize;\r
1767 \r
1768     /* Step 1: background is now black */\r
1769     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1770 \r
1771     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1772 \r
1773     pt.x = (squareSize - sz.cx) / 2;\r
1774     pt.y = (squareSize - sz.cy) / 2;\r
1775 \r
1776     SetBkMode( hdc, TRANSPARENT );\r
1777     SetTextColor( hdc, chroma );\r
1778     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1779     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1780 \r
1781     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1782     /* Step 3: the area outside the piece is filled with white */\r
1783 //    FloodFill( hdc, 0, 0, chroma );\r
1784     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1785     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1786     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1787     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1788     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1789     /* \r
1790         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1791         but if the start point is not inside the piece we're lost!\r
1792         There should be a better way to do this... if we could create a region or path\r
1793         from the fill operation we would be fine for example.\r
1794     */\r
1795 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1796     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1797 \r
1798     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1799         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1800         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1801 \r
1802         SelectObject( dc2, bm2 );\r
1803         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1804         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1805         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1806         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1807         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1808 \r
1809         DeleteDC( dc2 );\r
1810         DeleteObject( bm2 );\r
1811     }\r
1812 \r
1813     SetTextColor( hdc, 0 );\r
1814     /* \r
1815         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1816         draw the piece again in black for safety.\r
1817     */\r
1818     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1819 \r
1820     SelectObject( hdc, hbm_old );\r
1821 \r
1822     if( hPieceMask[index] != NULL ) {\r
1823         DeleteObject( hPieceMask[index] );\r
1824     }\r
1825 \r
1826     hPieceMask[index] = hbm;\r
1827 \r
1828     /* Face */\r
1829     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1830 \r
1831     SelectObject( hdc, hbm );\r
1832 \r
1833     {\r
1834         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1835         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1836         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1837 \r
1838         SelectObject( dc1, hPieceMask[index] );\r
1839         SelectObject( dc2, bm2 );\r
1840         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1841         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1842         \r
1843         /* \r
1844             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1845             the piece background and deletes (makes transparent) the rest.\r
1846             Thanks to that mask, we are free to paint the background with the greates\r
1847             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1848             We use this, to make gradients and give the pieces a "roundish" look.\r
1849         */\r
1850         SetPieceBackground( hdc, backColor, 2 );\r
1851         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1852 \r
1853         DeleteDC( dc2 );\r
1854         DeleteDC( dc1 );\r
1855         DeleteObject( bm2 );\r
1856     }\r
1857 \r
1858     SetTextColor( hdc, foreColor );\r
1859     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1860 \r
1861     SelectObject( hdc, hbm_old );\r
1862 \r
1863     if( hPieceFace[index] != NULL ) {\r
1864         DeleteObject( hPieceFace[index] );\r
1865     }\r
1866 \r
1867     hPieceFace[index] = hbm;\r
1868 }\r
1869 \r
1870 static int TranslatePieceToFontPiece( int piece )\r
1871 {\r
1872     switch( piece ) {\r
1873     case BlackPawn:\r
1874         return PM_BP;\r
1875     case BlackKnight:\r
1876         return PM_BN;\r
1877     case BlackBishop:\r
1878         return PM_BB;\r
1879     case BlackRook:\r
1880         return PM_BR;\r
1881     case BlackQueen:\r
1882         return PM_BQ;\r
1883     case BlackKing:\r
1884         return PM_BK;\r
1885     case WhitePawn:\r
1886         return PM_WP;\r
1887     case WhiteKnight:\r
1888         return PM_WN;\r
1889     case WhiteBishop:\r
1890         return PM_WB;\r
1891     case WhiteRook:\r
1892         return PM_WR;\r
1893     case WhiteQueen:\r
1894         return PM_WQ;\r
1895     case WhiteKing:\r
1896         return PM_WK;\r
1897 \r
1898     case BlackAngel:\r
1899         return PM_BA;\r
1900     case BlackMarshall:\r
1901         return PM_BC;\r
1902     case BlackFerz:\r
1903         return PM_BF;\r
1904     case BlackNightrider:\r
1905         return PM_BH;\r
1906     case BlackAlfil:\r
1907         return PM_BE;\r
1908     case BlackWazir:\r
1909         return PM_BW;\r
1910     case BlackUnicorn:\r
1911         return PM_BU;\r
1912     case BlackCannon:\r
1913         return PM_BO;\r
1914     case BlackGrasshopper:\r
1915         return PM_BG;\r
1916     case BlackMan:\r
1917         return PM_BM;\r
1918     case BlackSilver:\r
1919         return PM_BSG;\r
1920     case BlackLance:\r
1921         return PM_BL;\r
1922     case BlackFalcon:\r
1923         return PM_BV;\r
1924     case BlackCobra:\r
1925         return PM_BS;\r
1926     case BlackCardinal:\r
1927         return PM_BAB;\r
1928     case BlackDragon:\r
1929         return PM_BD;\r
1930 \r
1931     case WhiteAngel:\r
1932         return PM_WA;\r
1933     case WhiteMarshall:\r
1934         return PM_WC;\r
1935     case WhiteFerz:\r
1936         return PM_WF;\r
1937     case WhiteNightrider:\r
1938         return PM_WH;\r
1939     case WhiteAlfil:\r
1940         return PM_WE;\r
1941     case WhiteWazir:\r
1942         return PM_WW;\r
1943     case WhiteUnicorn:\r
1944         return PM_WU;\r
1945     case WhiteCannon:\r
1946         return PM_WO;\r
1947     case WhiteGrasshopper:\r
1948         return PM_WG;\r
1949     case WhiteMan:\r
1950         return PM_WM;\r
1951     case WhiteSilver:\r
1952         return PM_WSG;\r
1953     case WhiteLance:\r
1954         return PM_WL;\r
1955     case WhiteFalcon:\r
1956         return PM_WV;\r
1957     case WhiteCobra:\r
1958         return PM_WS;\r
1959     case WhiteCardinal:\r
1960         return PM_WAB;\r
1961     case WhiteDragon:\r
1962         return PM_WD;\r
1963     }\r
1964 \r
1965     return 0;\r
1966 }\r
1967 \r
1968 void CreatePiecesFromFont()\r
1969 {\r
1970     LOGFONT lf;\r
1971     HDC hdc_window = NULL;\r
1972     HDC hdc = NULL;\r
1973     HFONT hfont_old;\r
1974     int fontHeight;\r
1975     int i;\r
1976 \r
1977     if( fontBitmapSquareSize < 0 ) {\r
1978         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1979         return;\r
1980     }\r
1981 \r
1982     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1983         fontBitmapSquareSize = -1;\r
1984         return;\r
1985     }\r
1986 \r
1987     if( fontBitmapSquareSize != squareSize ) {\r
1988         hdc_window = GetDC( hwndMain );\r
1989         hdc = CreateCompatibleDC( hdc_window );\r
1990 \r
1991         if( hPieceFont != NULL ) {\r
1992             DeleteObject( hPieceFont );\r
1993         }\r
1994         else {\r
1995             for( i=0; i<=(int)BlackKing; i++ ) {\r
1996                 hPieceMask[i] = NULL;\r
1997                 hPieceFace[i] = NULL;\r
1998             }\r
1999         }\r
2000 \r
2001         fontHeight = 75;\r
2002 \r
2003         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2004             fontHeight = appData.fontPieceSize;\r
2005         }\r
2006 \r
2007         fontHeight = (fontHeight * squareSize) / 100;\r
2008 \r
2009         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2010         lf.lfWidth = 0;\r
2011         lf.lfEscapement = 0;\r
2012         lf.lfOrientation = 0;\r
2013         lf.lfWeight = FW_NORMAL;\r
2014         lf.lfItalic = 0;\r
2015         lf.lfUnderline = 0;\r
2016         lf.lfStrikeOut = 0;\r
2017         lf.lfCharSet = DEFAULT_CHARSET;\r
2018         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2019         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2020         lf.lfQuality = PROOF_QUALITY;\r
2021         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2022         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2023         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2024 \r
2025         hPieceFont = CreateFontIndirect( &lf );\r
2026 \r
2027         if( hPieceFont == NULL ) {\r
2028             fontBitmapSquareSize = -2;\r
2029         }\r
2030         else {\r
2031             /* Setup font-to-piece character table */\r
2032             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2033                 /* No (or wrong) global settings, try to detect the font */\r
2034                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2035                     /* Alpha */\r
2036                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2037                 }\r
2038                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2039                     /* DiagramTT* family */\r
2040                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2041                 }\r
2042                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2043                     /* Fairy symbols */\r
2044                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2045                 }\r
2046                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2047                     /* Good Companion (Some characters get warped as literal :-( */\r
2048                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2049                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2050                     SetCharTable(pieceToFontChar, s);\r
2051                 }\r
2052                 else {\r
2053                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2054                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2055                 }\r
2056             }\r
2057 \r
2058             /* Create bitmaps */\r
2059             hfont_old = SelectObject( hdc, hPieceFont );\r
2060             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2061                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2062                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2063 \r
2064             SelectObject( hdc, hfont_old );\r
2065 \r
2066             fontBitmapSquareSize = squareSize;\r
2067         }\r
2068     }\r
2069 \r
2070     if( hdc != NULL ) {\r
2071         DeleteDC( hdc );\r
2072     }\r
2073 \r
2074     if( hdc_window != NULL ) {\r
2075         ReleaseDC( hwndMain, hdc_window );\r
2076     }\r
2077 }\r
2078 \r
2079 HBITMAP\r
2080 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2081 {\r
2082   char name[128];\r
2083 \r
2084     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2085   if (gameInfo.event &&\r
2086       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2087       strcmp(name, "k80s") == 0) {\r
2088     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2089   }\r
2090   return LoadBitmap(hinst, name);\r
2091 }\r
2092 \r
2093 \r
2094 /* Insert a color into the program's logical palette\r
2095    structure.  This code assumes the given color is\r
2096    the result of the RGB or PALETTERGB macro, and it\r
2097    knows how those macros work (which is documented).\r
2098 */\r
2099 VOID\r
2100 InsertInPalette(COLORREF color)\r
2101 {\r
2102   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2103 \r
2104   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2105     DisplayFatalError(_("Too many colors"), 0, 1);\r
2106     pLogPal->palNumEntries--;\r
2107     return;\r
2108   }\r
2109 \r
2110   pe->peFlags = (char) 0;\r
2111   pe->peRed = (char) (0xFF & color);\r
2112   pe->peGreen = (char) (0xFF & (color >> 8));\r
2113   pe->peBlue = (char) (0xFF & (color >> 16));\r
2114   return;\r
2115 }\r
2116 \r
2117 \r
2118 VOID\r
2119 InitDrawingColors()\r
2120 {\r
2121   if (pLogPal == NULL) {\r
2122     /* Allocate enough memory for a logical palette with\r
2123      * PALETTESIZE entries and set the size and version fields\r
2124      * of the logical palette structure.\r
2125      */\r
2126     pLogPal = (NPLOGPALETTE)\r
2127       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2128                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2129     pLogPal->palVersion    = 0x300;\r
2130   }\r
2131   pLogPal->palNumEntries = 0;\r
2132 \r
2133   InsertInPalette(lightSquareColor);\r
2134   InsertInPalette(darkSquareColor);\r
2135   InsertInPalette(whitePieceColor);\r
2136   InsertInPalette(blackPieceColor);\r
2137   InsertInPalette(highlightSquareColor);\r
2138   InsertInPalette(premoveHighlightColor);\r
2139 \r
2140   /*  create a logical color palette according the information\r
2141    *  in the LOGPALETTE structure.\r
2142    */\r
2143   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2144 \r
2145   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2146   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2147   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2148   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2149   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2150   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2151   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2152   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2153   /* [AS] Force rendering of the font-based pieces */\r
2154   if( fontBitmapSquareSize > 0 ) {\r
2155     fontBitmapSquareSize = 0;\r
2156   }\r
2157 }\r
2158 \r
2159 \r
2160 int\r
2161 BoardWidth(int boardSize, int n)\r
2162 { /* [HGM] argument n added to allow different width and height */\r
2163   int lineGap = sizeInfo[boardSize].lineGap;\r
2164 \r
2165   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2166       lineGap = appData.overrideLineGap;\r
2167   }\r
2168 \r
2169   return (n + 1) * lineGap +\r
2170           n * sizeInfo[boardSize].squareSize;\r
2171 }\r
2172 \r
2173 /* Respond to board resize by dragging edge */\r
2174 VOID\r
2175 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2176 {\r
2177   BoardSize newSize = NUM_SIZES - 1;\r
2178   static int recurse = 0;\r
2179   if (IsIconic(hwndMain)) return;\r
2180   if (recurse > 0) return;\r
2181   recurse++;\r
2182   while (newSize > 0) {\r
2183         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2184         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2185            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2186     newSize--;\r
2187   } \r
2188   boardSize = newSize;\r
2189   InitDrawingSizes(boardSize, flags);\r
2190   recurse--;\r
2191 }\r
2192 \r
2193 \r
2194 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2195 \r
2196 VOID\r
2197 InitDrawingSizes(BoardSize boardSize, int flags)\r
2198 {\r
2199   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2200   ChessSquare piece;\r
2201   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2202   HDC hdc;\r
2203   SIZE clockSize, messageSize;\r
2204   HFONT oldFont;\r
2205   char buf[MSG_SIZ];\r
2206   char *str;\r
2207   HMENU hmenu = GetMenu(hwndMain);\r
2208   RECT crect, wrect, oldRect;\r
2209   int offby;\r
2210   LOGBRUSH logbrush;\r
2211 \r
2212   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2213   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2214 \r
2215   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2216   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2217 \r
2218   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2219   oldRect.top = wpMain.y;\r
2220   oldRect.right = wpMain.x + wpMain.width;\r
2221   oldRect.bottom = wpMain.y + wpMain.height;\r
2222 \r
2223   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2224   smallLayout = sizeInfo[boardSize].smallLayout;\r
2225   squareSize = sizeInfo[boardSize].squareSize;\r
2226   lineGap = sizeInfo[boardSize].lineGap;\r
2227   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2228 \r
2229   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2230       lineGap = appData.overrideLineGap;\r
2231   }\r
2232 \r
2233   if (tinyLayout != oldTinyLayout) {\r
2234     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2235     if (tinyLayout) {\r
2236       style &= ~WS_SYSMENU;\r
2237       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2238                  "&Minimize\tCtrl+F4");\r
2239     } else {\r
2240       style |= WS_SYSMENU;\r
2241       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2242     }\r
2243     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2244 \r
2245     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2246       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2247         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2248     }\r
2249     DrawMenuBar(hwndMain);\r
2250   }\r
2251 \r
2252   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2253   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2254 \r
2255   /* Get text area sizes */\r
2256   hdc = GetDC(hwndMain);\r
2257   if (appData.clockMode) {\r
2258     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2259   } else {\r
2260     snprintf(buf, MSG_SIZ, _("White"));\r
2261   }\r
2262   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2263   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2264   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2265   str = _("We only care about the height here");\r
2266   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2267   SelectObject(hdc, oldFont);\r
2268   ReleaseDC(hwndMain, hdc);\r
2269 \r
2270   /* Compute where everything goes */\r
2271   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2272         /* [HGM] logo: if either logo is on, reserve space for it */\r
2273         logoHeight =  2*clockSize.cy;\r
2274         leftLogoRect.left   = OUTER_MARGIN;\r
2275         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2276         leftLogoRect.top    = OUTER_MARGIN;\r
2277         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2278 \r
2279         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2280         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2281         rightLogoRect.top    = OUTER_MARGIN;\r
2282         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2283 \r
2284 \r
2285     whiteRect.left = leftLogoRect.right;\r
2286     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2287     whiteRect.top = OUTER_MARGIN;\r
2288     whiteRect.bottom = whiteRect.top + logoHeight;\r
2289 \r
2290     blackRect.right = rightLogoRect.left;\r
2291     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2292     blackRect.top = whiteRect.top;\r
2293     blackRect.bottom = whiteRect.bottom;\r
2294   } else {\r
2295     whiteRect.left = OUTER_MARGIN;\r
2296     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2297     whiteRect.top = OUTER_MARGIN;\r
2298     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2299 \r
2300     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2301     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2302     blackRect.top = whiteRect.top;\r
2303     blackRect.bottom = whiteRect.bottom;\r
2304 \r
2305     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2306   }\r
2307 \r
2308   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2309   if (appData.showButtonBar) {\r
2310     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2311       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2312   } else {\r
2313     messageRect.right = OUTER_MARGIN + boardWidth;\r
2314   }\r
2315   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2316   messageRect.bottom = messageRect.top + messageSize.cy;\r
2317 \r
2318   boardRect.left = OUTER_MARGIN;\r
2319   boardRect.right = boardRect.left + boardWidth;\r
2320   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2321   boardRect.bottom = boardRect.top + boardHeight;\r
2322 \r
2323   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2324   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2325   oldBoardSize = boardSize;\r
2326   oldTinyLayout = tinyLayout;\r
2327   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2328   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2329     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2330   winW *= 1 + twoBoards;\r
2331   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2332   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2333   wpMain.height = winH; //       without disturbing window attachments\r
2334   GetWindowRect(hwndMain, &wrect);\r
2335   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2336                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2337 \r
2338   // [HGM] placement: let attached windows follow size change.\r
2339   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2340   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2341   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2342   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2343   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2344 \r
2345   /* compensate if menu bar wrapped */\r
2346   GetClientRect(hwndMain, &crect);\r
2347   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2348   wpMain.height += offby;\r
2349   switch (flags) {\r
2350   case WMSZ_TOPLEFT:\r
2351     SetWindowPos(hwndMain, NULL, \r
2352                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2353                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2354     break;\r
2355 \r
2356   case WMSZ_TOPRIGHT:\r
2357   case WMSZ_TOP:\r
2358     SetWindowPos(hwndMain, NULL, \r
2359                  wrect.left, wrect.bottom - wpMain.height, \r
2360                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2361     break;\r
2362 \r
2363   case WMSZ_BOTTOMLEFT:\r
2364   case WMSZ_LEFT:\r
2365     SetWindowPos(hwndMain, NULL, \r
2366                  wrect.right - wpMain.width, wrect.top, \r
2367                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2368     break;\r
2369 \r
2370   case WMSZ_BOTTOMRIGHT:\r
2371   case WMSZ_BOTTOM:\r
2372   case WMSZ_RIGHT:\r
2373   default:\r
2374     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2375                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2376     break;\r
2377   }\r
2378 \r
2379   hwndPause = NULL;\r
2380   for (i = 0; i < N_BUTTONS; i++) {\r
2381     if (buttonDesc[i].hwnd != NULL) {\r
2382       DestroyWindow(buttonDesc[i].hwnd);\r
2383       buttonDesc[i].hwnd = NULL;\r
2384     }\r
2385     if (appData.showButtonBar) {\r
2386       buttonDesc[i].hwnd =\r
2387         CreateWindow("BUTTON", buttonDesc[i].label,\r
2388                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2389                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2390                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2391                      (HMENU) buttonDesc[i].id,\r
2392                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2393       if (tinyLayout) {\r
2394         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2395                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2396                     MAKELPARAM(FALSE, 0));\r
2397       }\r
2398       if (buttonDesc[i].id == IDM_Pause)\r
2399         hwndPause = buttonDesc[i].hwnd;\r
2400       buttonDesc[i].wndproc = (WNDPROC)\r
2401         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2402     }\r
2403   }\r
2404   if (gridPen != NULL) DeleteObject(gridPen);\r
2405   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2406   if (premovePen != NULL) DeleteObject(premovePen);\r
2407   if (lineGap != 0) {\r
2408     logbrush.lbStyle = BS_SOLID;\r
2409     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2410     gridPen =\r
2411       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2412                    lineGap, &logbrush, 0, NULL);\r
2413     logbrush.lbColor = highlightSquareColor;\r
2414     highlightPen =\r
2415       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2416                    lineGap, &logbrush, 0, NULL);\r
2417 \r
2418     logbrush.lbColor = premoveHighlightColor; \r
2419     premovePen =\r
2420       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2421                    lineGap, &logbrush, 0, NULL);\r
2422 \r
2423     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2424     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2425       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2426       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2427         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2428       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2429         BOARD_WIDTH * (squareSize + lineGap);\r
2430       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2431     }\r
2432     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2433       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2434       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2435         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2436         lineGap / 2 + (i * (squareSize + lineGap));\r
2437       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2438         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2439       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2440     }\r
2441   }\r
2442 \r
2443   /* [HGM] Licensing requirement */\r
2444 #ifdef GOTHIC\r
2445   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2446 #endif\r
2447 #ifdef FALCON\r
2448   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2449 #endif\r
2450   GothicPopUp( "", VariantNormal);\r
2451 \r
2452 \r
2453 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2454 \r
2455   /* Load piece bitmaps for this board size */\r
2456   for (i=0; i<=2; i++) {\r
2457     for (piece = WhitePawn;\r
2458          (int) piece < (int) BlackPawn;\r
2459          piece = (ChessSquare) ((int) piece + 1)) {\r
2460       if (pieceBitmap[i][piece] != NULL)\r
2461         DeleteObject(pieceBitmap[i][piece]);\r
2462     }\r
2463   }\r
2464 \r
2465   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2466   // Orthodox Chess pieces\r
2467   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2468   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2469   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2470   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2471   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2472   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2473   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2474   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2475   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2476   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2477   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2478   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2479   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2480   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2481   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2482   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2483     // in Shogi, Hijack the unused Queen for Lance\r
2484     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2485     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2486     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2487   } else {\r
2488     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2489     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2490     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2491   }\r
2492 \r
2493   if(squareSize <= 72 && squareSize >= 33) { \r
2494     /* A & C are available in most sizes now */\r
2495     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2496       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2497       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2498       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2499       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2500       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2501       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2502       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2503       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2504       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2505       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2506       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2507       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2508     } else { // Smirf-like\r
2509       if(gameInfo.variant == VariantSChess) {\r
2510         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2511         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2512         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2513       } else {\r
2514         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2515         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2516         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2517       }\r
2518     }\r
2519     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2520       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2521       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2522       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2523     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2524       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2525       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2526       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2527     } else { // WinBoard standard\r
2528       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2529       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2530       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2531     }\r
2532   }\r
2533 \r
2534 \r
2535   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2536     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2537     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2538     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2539     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2540     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2541     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2542     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2543     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2544     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2545     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2546     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2547     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2548     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2549     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2550     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2551     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2552     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2553     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2554     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2555     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2556     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2557     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2558     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2559     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2560     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2561     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2562     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2563     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2564     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2565     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2566 \r
2567     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2568       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2569       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2570       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2571       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2572       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2573       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2574       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2575       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2576       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2577       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2578       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2579       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2580     } else {\r
2581       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2582       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2583       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2584       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2585       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2586       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2587       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2588       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2589       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2590       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2591       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2592       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2593     }\r
2594 \r
2595   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2596     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2597     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2598     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2599     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2600     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2601     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2602     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2603     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2604     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2605     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2606     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2607     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2608     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2609     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2610   }\r
2611 \r
2612 \r
2613   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2614   /* special Shogi support in this size */\r
2615   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2616       for (piece = WhitePawn;\r
2617            (int) piece < (int) BlackPawn;\r
2618            piece = (ChessSquare) ((int) piece + 1)) {\r
2619         if (pieceBitmap[i][piece] != NULL)\r
2620           DeleteObject(pieceBitmap[i][piece]);\r
2621       }\r
2622     }\r
2623   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2624   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2625   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2626   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2627   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2628   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2629   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2630   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2631   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2632   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2633   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2634   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2635   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2636   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2637   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2638   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2639   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2640   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2641   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2642   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2643   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2644   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2645   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2646   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2647   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2648   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2649   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2650   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2651   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2652   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2653   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2654   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2655   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2656   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2657   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2658   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2659   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2660   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2661   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2662   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2663   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2664   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2665   minorSize = 0;\r
2666   }\r
2667 }\r
2668 \r
2669 HBITMAP\r
2670 PieceBitmap(ChessSquare p, int kind)\r
2671 {\r
2672   if ((int) p >= (int) BlackPawn)\r
2673     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2674 \r
2675   return pieceBitmap[kind][(int) p];\r
2676 }\r
2677 \r
2678 /***************************************************************/\r
2679 \r
2680 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2681 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2682 /*\r
2683 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2684 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2685 */\r
2686 \r
2687 VOID\r
2688 SquareToPos(int row, int column, int * x, int * y)\r
2689 {\r
2690   if (flipView) {\r
2691     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2692     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2693   } else {\r
2694     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2695     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2696   }\r
2697 }\r
2698 \r
2699 VOID\r
2700 DrawCoordsOnDC(HDC hdc)\r
2701 {\r
2702   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
2703   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
2704   char str[2] = { NULLCHAR, NULLCHAR };\r
2705   int oldMode, oldAlign, x, y, start, i;\r
2706   HFONT oldFont;\r
2707   HBRUSH oldBrush;\r
2708 \r
2709   if (!appData.showCoords)\r
2710     return;\r
2711 \r
2712   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2713 \r
2714   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2715   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2716   oldAlign = GetTextAlign(hdc);\r
2717   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2718 \r
2719   y = boardRect.top + lineGap;\r
2720   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2721 \r
2722   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2723   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2724     str[0] = files[start + i];\r
2725     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2726     y += squareSize + lineGap;\r
2727   }\r
2728 \r
2729   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2730 \r
2731   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2732   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2733     str[0] = ranks[start + i];\r
2734     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2735     x += squareSize + lineGap;\r
2736   }    \r
2737 \r
2738   SelectObject(hdc, oldBrush);\r
2739   SetBkMode(hdc, oldMode);\r
2740   SetTextAlign(hdc, oldAlign);\r
2741   SelectObject(hdc, oldFont);\r
2742 }\r
2743 \r
2744 VOID\r
2745 DrawGridOnDC(HDC hdc)\r
2746 {\r
2747   HPEN oldPen;\r
2748  \r
2749   if (lineGap != 0) {\r
2750     oldPen = SelectObject(hdc, gridPen);\r
2751     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2752     SelectObject(hdc, oldPen);\r
2753   }\r
2754 }\r
2755 \r
2756 #define HIGHLIGHT_PEN 0\r
2757 #define PREMOVE_PEN   1\r
2758 \r
2759 VOID\r
2760 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2761 {\r
2762   int x1, y1;\r
2763   HPEN oldPen, hPen;\r
2764   if (lineGap == 0) return;\r
2765   if (flipView) {\r
2766     x1 = boardRect.left +\r
2767       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2768     y1 = boardRect.top +\r
2769       lineGap/2 + y * (squareSize + lineGap);\r
2770   } else {\r
2771     x1 = boardRect.left +\r
2772       lineGap/2 + x * (squareSize + lineGap);\r
2773     y1 = boardRect.top +\r
2774       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2775   }\r
2776   hPen = pen ? premovePen : highlightPen;\r
2777   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2778   MoveToEx(hdc, x1, y1, NULL);\r
2779   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2780   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2781   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2782   LineTo(hdc, x1, y1);\r
2783   SelectObject(hdc, oldPen);\r
2784 }\r
2785 \r
2786 VOID\r
2787 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2788 {\r
2789   int i;\r
2790   for (i=0; i<2; i++) {\r
2791     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2792       DrawHighlightOnDC(hdc, TRUE,\r
2793                         h->sq[i].x, h->sq[i].y,\r
2794                         pen);\r
2795   }\r
2796 }\r
2797 \r
2798 /* Note: sqcolor is used only in monoMode */\r
2799 /* Note that this code is largely duplicated in woptions.c,\r
2800    function DrawSampleSquare, so that needs to be updated too */\r
2801 VOID\r
2802 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2803 {\r
2804   HBITMAP oldBitmap;\r
2805   HBRUSH oldBrush;\r
2806   int tmpSize;\r
2807 \r
2808   if (appData.blindfold) return;\r
2809 \r
2810   /* [AS] Use font-based pieces if needed */\r
2811   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2812     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2813     CreatePiecesFromFont();\r
2814 \r
2815     if( fontBitmapSquareSize == squareSize ) {\r
2816         int index = TranslatePieceToFontPiece(piece);\r
2817 \r
2818         SelectObject( tmphdc, hPieceMask[ index ] );\r
2819 \r
2820       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2821         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2822       else\r
2823         BitBlt( hdc,\r
2824             x, y,\r
2825             squareSize, squareSize,\r
2826             tmphdc,\r
2827             0, 0,\r
2828             SRCAND );\r
2829 \r
2830         SelectObject( tmphdc, hPieceFace[ index ] );\r
2831 \r
2832       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2833         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2834       else\r
2835         BitBlt( hdc,\r
2836             x, y,\r
2837             squareSize, squareSize,\r
2838             tmphdc,\r
2839             0, 0,\r
2840             SRCPAINT );\r
2841 \r
2842         return;\r
2843     }\r
2844   }\r
2845 \r
2846   if (appData.monoMode) {\r
2847     SelectObject(tmphdc, PieceBitmap(piece, \r
2848       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2849     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2850            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2851   } else {\r
2852     tmpSize = squareSize;\r
2853     if(minorSize &&\r
2854         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2855          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2856       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2857       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2858       x += (squareSize - minorSize)>>1;\r
2859       y += squareSize - minorSize - 2;\r
2860       tmpSize = minorSize;\r
2861     }\r
2862     if (color || appData.allWhite ) {\r
2863       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2864       if( color )\r
2865               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2866       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2867       if(appData.upsideDown && color==flipView)\r
2868         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2869       else\r
2870         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2871       /* Use black for outline of white pieces */\r
2872       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2873       if(appData.upsideDown && color==flipView)\r
2874         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2875       else\r
2876         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2877     } else {\r
2878       /* Use square color for details of black pieces */\r
2879       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2880       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2881       if(appData.upsideDown && !flipView)\r
2882         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2883       else\r
2884         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2885     }\r
2886     SelectObject(hdc, oldBrush);\r
2887     SelectObject(tmphdc, oldBitmap);\r
2888   }\r
2889 }\r
2890 \r
2891 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2892 int GetBackTextureMode( int algo )\r
2893 {\r
2894     int result = BACK_TEXTURE_MODE_DISABLED;\r
2895 \r
2896     switch( algo ) \r
2897     {\r
2898         case BACK_TEXTURE_MODE_PLAIN:\r
2899             result = 1; /* Always use identity map */\r
2900             break;\r
2901         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2902             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2903             break;\r
2904     }\r
2905 \r
2906     return result;\r
2907 }\r
2908 \r
2909 /* \r
2910     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2911     to handle redraws cleanly (as random numbers would always be different).\r
2912 */\r
2913 VOID RebuildTextureSquareInfo()\r
2914 {\r
2915     BITMAP bi;\r
2916     int lite_w = 0;\r
2917     int lite_h = 0;\r
2918     int dark_w = 0;\r
2919     int dark_h = 0;\r
2920     int row;\r
2921     int col;\r
2922 \r
2923     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2924 \r
2925     if( liteBackTexture != NULL ) {\r
2926         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2927             lite_w = bi.bmWidth;\r
2928             lite_h = bi.bmHeight;\r
2929         }\r
2930     }\r
2931 \r
2932     if( darkBackTexture != NULL ) {\r
2933         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2934             dark_w = bi.bmWidth;\r
2935             dark_h = bi.bmHeight;\r
2936         }\r
2937     }\r
2938 \r
2939     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2940         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2941             if( (col + row) & 1 ) {\r
2942                 /* Lite square */\r
2943                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2944                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2945                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2946                   else\r
2947                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2948                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2949                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2950                   else\r
2951                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2952                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2953                 }\r
2954             }\r
2955             else {\r
2956                 /* Dark square */\r
2957                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2958                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2959                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2960                   else\r
2961                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2962                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2963                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2964                   else\r
2965                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2966                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2967                 }\r
2968             }\r
2969         }\r
2970     }\r
2971 }\r
2972 \r
2973 /* [AS] Arrow highlighting support */\r
2974 \r
2975 static double A_WIDTH = 5; /* Width of arrow body */\r
2976 \r
2977 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2978 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2979 \r
2980 static double Sqr( double x )\r
2981 {\r
2982     return x*x;\r
2983 }\r
2984 \r
2985 static int Round( double x )\r
2986 {\r
2987     return (int) (x + 0.5);\r
2988 }\r
2989 \r
2990 /* Draw an arrow between two points using current settings */\r
2991 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2992 {\r
2993     POINT arrow[7];\r
2994     double dx, dy, j, k, x, y;\r
2995 \r
2996     if( d_x == s_x ) {\r
2997         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2998 \r
2999         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3000         arrow[0].y = s_y;\r
3001 \r
3002         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3003         arrow[1].y = d_y - h;\r
3004 \r
3005         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3006         arrow[2].y = d_y - h;\r
3007 \r
3008         arrow[3].x = d_x;\r
3009         arrow[3].y = d_y;\r
3010 \r
3011         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3012         arrow[5].y = d_y - h;\r
3013 \r
3014         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3015         arrow[4].y = d_y - h;\r
3016 \r
3017         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3018         arrow[6].y = s_y;\r
3019     }\r
3020     else if( d_y == s_y ) {\r
3021         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3022 \r
3023         arrow[0].x = s_x;\r
3024         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3025 \r
3026         arrow[1].x = d_x - w;\r
3027         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3028 \r
3029         arrow[2].x = d_x - w;\r
3030         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3031 \r
3032         arrow[3].x = d_x;\r
3033         arrow[3].y = d_y;\r
3034 \r
3035         arrow[5].x = d_x - w;\r
3036         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3037 \r
3038         arrow[4].x = d_x - w;\r
3039         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3040 \r
3041         arrow[6].x = s_x;\r
3042         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3043     }\r
3044     else {\r
3045         /* [AS] Needed a lot of paper for this! :-) */\r
3046         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3047         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3048   \r
3049         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3050 \r
3051         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3052 \r
3053         x = s_x;\r
3054         y = s_y;\r
3055 \r
3056         arrow[0].x = Round(x - j);\r
3057         arrow[0].y = Round(y + j*dx);\r
3058 \r
3059         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3060         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3061 \r
3062         if( d_x > s_x ) {\r
3063             x = (double) d_x - k;\r
3064             y = (double) d_y - k*dy;\r
3065         }\r
3066         else {\r
3067             x = (double) d_x + k;\r
3068             y = (double) d_y + k*dy;\r
3069         }\r
3070 \r
3071         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3072 \r
3073         arrow[6].x = Round(x - j);\r
3074         arrow[6].y = Round(y + j*dx);\r
3075 \r
3076         arrow[2].x = Round(arrow[6].x + 2*j);\r
3077         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3078 \r
3079         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3080         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3081 \r
3082         arrow[4].x = d_x;\r
3083         arrow[4].y = d_y;\r
3084 \r
3085         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3086         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3087     }\r
3088 \r
3089     Polygon( hdc, arrow, 7 );\r
3090 }\r
3091 \r
3092 /* [AS] Draw an arrow between two squares */\r
3093 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3094 {\r
3095     int s_x, s_y, d_x, d_y;\r
3096     HPEN hpen;\r
3097     HPEN holdpen;\r
3098     HBRUSH hbrush;\r
3099     HBRUSH holdbrush;\r
3100     LOGBRUSH stLB;\r
3101 \r
3102     if( s_col == d_col && s_row == d_row ) {\r
3103         return;\r
3104     }\r
3105 \r
3106     /* Get source and destination points */\r
3107     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3108     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3109 \r
3110     if( d_y > s_y ) {\r
3111         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3112     }\r
3113     else if( d_y < s_y ) {\r
3114         d_y += squareSize / 2 + squareSize / 4;\r
3115     }\r
3116     else {\r
3117         d_y += squareSize / 2;\r
3118     }\r
3119 \r
3120     if( d_x > s_x ) {\r
3121         d_x += squareSize / 2 - squareSize / 4;\r
3122     }\r
3123     else if( d_x < s_x ) {\r
3124         d_x += squareSize / 2 + squareSize / 4;\r
3125     }\r
3126     else {\r
3127         d_x += squareSize / 2;\r
3128     }\r
3129 \r
3130     s_x += squareSize / 2;\r
3131     s_y += squareSize / 2;\r
3132 \r
3133     /* Adjust width */\r
3134     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3135 \r
3136     /* Draw */\r
3137     stLB.lbStyle = BS_SOLID;\r
3138     stLB.lbColor = appData.highlightArrowColor;\r
3139     stLB.lbHatch = 0;\r
3140 \r
3141     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3142     holdpen = SelectObject( hdc, hpen );\r
3143     hbrush = CreateBrushIndirect( &stLB );\r
3144     holdbrush = SelectObject( hdc, hbrush );\r
3145 \r
3146     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3147 \r
3148     SelectObject( hdc, holdpen );\r
3149     SelectObject( hdc, holdbrush );\r
3150     DeleteObject( hpen );\r
3151     DeleteObject( hbrush );\r
3152 }\r
3153 \r
3154 BOOL HasHighlightInfo()\r
3155 {\r
3156     BOOL result = FALSE;\r
3157 \r
3158     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3159         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3160     {\r
3161         result = TRUE;\r
3162     }\r
3163 \r
3164     return result;\r
3165 }\r
3166 \r
3167 BOOL IsDrawArrowEnabled()\r
3168 {\r
3169     BOOL result = FALSE;\r
3170 \r
3171     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3172         result = TRUE;\r
3173     }\r
3174 \r
3175     return result;\r
3176 }\r
3177 \r
3178 VOID DrawArrowHighlight( HDC hdc )\r
3179 {\r
3180     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3181         DrawArrowBetweenSquares( hdc,\r
3182             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3183             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3184     }\r
3185 }\r
3186 \r
3187 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3188 {\r
3189     HRGN result = NULL;\r
3190 \r
3191     if( HasHighlightInfo() ) {\r
3192         int x1, y1, x2, y2;\r
3193         int sx, sy, dx, dy;\r
3194 \r
3195         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3196         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3197 \r
3198         sx = MIN( x1, x2 );\r
3199         sy = MIN( y1, y2 );\r
3200         dx = MAX( x1, x2 ) + squareSize;\r
3201         dy = MAX( y1, y2 ) + squareSize;\r
3202 \r
3203         result = CreateRectRgn( sx, sy, dx, dy );\r
3204     }\r
3205 \r
3206     return result;\r
3207 }\r
3208 \r
3209 /*\r
3210     Warning: this function modifies the behavior of several other functions. \r
3211     \r
3212     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3213     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3214     repaint is scattered all over the place, which is not good for features such as\r
3215     "arrow highlighting" that require a full repaint of the board.\r
3216 \r
3217     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3218     user interaction, when speed is not so important) but especially to avoid errors\r
3219     in the displayed graphics.\r
3220 \r
3221     In such patched places, I always try refer to this function so there is a single\r
3222     place to maintain knowledge.\r
3223     \r
3224     To restore the original behavior, just return FALSE unconditionally.\r
3225 */\r
3226 BOOL IsFullRepaintPreferrable()\r
3227 {\r
3228     BOOL result = FALSE;\r
3229 \r
3230     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3231         /* Arrow may appear on the board */\r
3232         result = TRUE;\r
3233     }\r
3234 \r
3235     return result;\r
3236 }\r
3237 \r
3238 /* \r
3239     This function is called by DrawPosition to know whether a full repaint must\r
3240     be forced or not.\r
3241 \r
3242     Only DrawPosition may directly call this function, which makes use of \r
3243     some state information. Other function should call DrawPosition specifying \r
3244     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3245 */\r
3246 BOOL DrawPositionNeedsFullRepaint()\r
3247 {\r
3248     BOOL result = FALSE;\r
3249 \r
3250     /* \r
3251         Probably a slightly better policy would be to trigger a full repaint\r
3252         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3253         but animation is fast enough that it's difficult to notice.\r
3254     */\r
3255     if( animInfo.piece == EmptySquare ) {\r
3256         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3257             result = TRUE;\r
3258         }\r
3259     }\r
3260 \r
3261     return result;\r
3262 }\r
3263 \r
3264 VOID\r
3265 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3266 {\r
3267   int row, column, x, y, square_color, piece_color;\r
3268   ChessSquare piece;\r
3269   HBRUSH oldBrush;\r
3270   HDC texture_hdc = NULL;\r
3271 \r
3272   /* [AS] Initialize background textures if needed */\r
3273   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3274       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3275       if( backTextureSquareSize != squareSize \r
3276        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3277           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3278           backTextureSquareSize = squareSize;\r
3279           RebuildTextureSquareInfo();\r
3280       }\r
3281 \r
3282       texture_hdc = CreateCompatibleDC( hdc );\r
3283   }\r
3284 \r
3285   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3286     for (column = 0; column < BOARD_WIDTH; column++) {\r
3287   \r
3288       SquareToPos(row, column, &x, &y);\r
3289 \r
3290       piece = board[row][column];\r
3291 \r
3292       square_color = ((column + row) % 2) == 1;\r
3293       if( gameInfo.variant == VariantXiangqi ) {\r
3294           square_color = !InPalace(row, column);\r
3295           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3296           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3297       }\r
3298       piece_color = (int) piece < (int) BlackPawn;\r
3299 \r
3300 \r
3301       /* [HGM] holdings file: light square or black */\r
3302       if(column == BOARD_LEFT-2) {\r
3303             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3304                 square_color = 1;\r
3305             else {\r
3306                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3307                 continue;\r
3308             }\r
3309       } else\r
3310       if(column == BOARD_RGHT + 1 ) {\r
3311             if( row < gameInfo.holdingsSize )\r
3312                 square_color = 1;\r
3313             else {\r
3314                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3315                 continue;\r
3316             }\r
3317       }\r
3318       if(column == BOARD_LEFT-1 ) /* left align */\r
3319             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3320       else if( column == BOARD_RGHT) /* right align */\r
3321             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3322       else\r
3323       if (appData.monoMode) {\r
3324         if (piece == EmptySquare) {\r
3325           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3326                  square_color ? WHITENESS : BLACKNESS);\r
3327         } else {\r
3328           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3329         }\r
3330       } \r
3331       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3332           /* [AS] Draw the square using a texture bitmap */\r
3333           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3334           int r = row, c = column; // [HGM] do not flip board in flipView\r
3335           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3336 \r
3337           DrawTile( x, y, \r
3338               squareSize, squareSize, \r
3339               hdc, \r
3340               texture_hdc,\r
3341               backTextureSquareInfo[r][c].mode,\r
3342               backTextureSquareInfo[r][c].x,\r
3343               backTextureSquareInfo[r][c].y );\r
3344 \r
3345           SelectObject( texture_hdc, hbm );\r
3346 \r
3347           if (piece != EmptySquare) {\r
3348               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3349           }\r
3350       }\r
3351       else {\r
3352         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3353 \r
3354         oldBrush = SelectObject(hdc, brush );\r
3355         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3356         SelectObject(hdc, oldBrush);\r
3357         if (piece != EmptySquare)\r
3358           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3359       }\r
3360     }\r
3361   }\r
3362 \r
3363   if( texture_hdc != NULL ) {\r
3364     DeleteDC( texture_hdc );\r
3365   }\r
3366 }\r
3367 \r
3368 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3369 void fputDW(FILE *f, int x)\r
3370 {\r
3371         fputc(x     & 255, f);\r
3372         fputc(x>>8  & 255, f);\r
3373         fputc(x>>16 & 255, f);\r
3374         fputc(x>>24 & 255, f);\r
3375 }\r
3376 \r
3377 #define MAX_CLIPS 200   /* more than enough */\r
3378 \r
3379 VOID\r
3380 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3381 {\r
3382 //  HBITMAP bufferBitmap;\r
3383   BITMAP bi;\r
3384 //  RECT Rect;\r
3385   HDC tmphdc;\r
3386   HBITMAP hbm;\r
3387   int w = 100, h = 50;\r
3388 \r
3389   if(logo == NULL) return;\r
3390 //  GetClientRect(hwndMain, &Rect);\r
3391 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3392 //                                      Rect.bottom-Rect.top+1);\r
3393   tmphdc = CreateCompatibleDC(hdc);\r
3394   hbm = SelectObject(tmphdc, logo);\r
3395   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3396             w = bi.bmWidth;\r
3397             h = bi.bmHeight;\r
3398   }\r
3399   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3400                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3401   SelectObject(tmphdc, hbm);\r
3402   DeleteDC(tmphdc);\r
3403 }\r
3404 \r
3405 VOID\r
3406 DisplayLogos()\r
3407 {\r
3408   if(logoHeight) {\r
3409         HDC hdc = GetDC(hwndMain);\r
3410         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3411         if(appData.autoLogo) {\r
3412           \r
3413           switch(gameMode) { // pick logos based on game mode\r
3414             case IcsObserving:\r
3415                 whiteLogo = second.programLogo; // ICS logo\r
3416                 blackLogo = second.programLogo;\r
3417             default:\r
3418                 break;\r
3419             case IcsPlayingWhite:\r
3420                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3421                 blackLogo = second.programLogo; // ICS logo\r
3422                 break;\r
3423             case IcsPlayingBlack:\r
3424                 whiteLogo = second.programLogo; // ICS logo\r
3425                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3426                 break;\r
3427             case TwoMachinesPlay:\r
3428                 if(first.twoMachinesColor[0] == 'b') {\r
3429                     whiteLogo = second.programLogo;\r
3430                     blackLogo = first.programLogo;\r
3431                 }\r
3432                 break;\r
3433             case MachinePlaysWhite:\r
3434                 blackLogo = userLogo;\r
3435                 break;\r
3436             case MachinePlaysBlack:\r
3437                 whiteLogo = userLogo;\r
3438                 blackLogo = first.programLogo;\r
3439           }\r
3440         }\r
3441         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3442         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3443         ReleaseDC(hwndMain, hdc);\r
3444   }\r
3445 }\r
3446 \r
3447 static HDC hdcSeek;\r
3448 \r
3449 // [HGM] seekgraph\r
3450 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3451 {\r
3452     POINT stPt;\r
3453     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3454     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3455     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3456     SelectObject( hdcSeek, hp );\r
3457 }\r
3458 \r
3459 // front-end wrapper for drawing functions to do rectangles\r
3460 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3461 {\r
3462     HPEN hp;\r
3463     RECT rc;\r
3464 \r
3465     if (hdcSeek == NULL) {\r
3466     hdcSeek = GetDC(hwndMain);\r
3467       if (!appData.monoMode) {\r
3468         SelectPalette(hdcSeek, hPal, FALSE);\r
3469         RealizePalette(hdcSeek);\r
3470       }\r
3471     }\r
3472     hp = SelectObject( hdcSeek, gridPen );\r
3473     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3474     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3475     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3476     SelectObject( hdcSeek, hp );\r
3477 }\r
3478 \r
3479 // front-end wrapper for putting text in graph\r
3480 void DrawSeekText(char *buf, int x, int y)\r
3481 {\r
3482         SIZE stSize;\r
3483         SetBkMode( hdcSeek, TRANSPARENT );\r
3484         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3485         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3486 }\r
3487 \r
3488 void DrawSeekDot(int x, int y, int color)\r
3489 {\r
3490         int square = color & 0x80;\r
3491         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3492                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3493         color &= 0x7F;\r
3494         if(square)\r
3495             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3496                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3497         else\r
3498             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3499                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3500             SelectObject(hdcSeek, oldBrush);\r
3501 }\r
3502 \r
3503 VOID\r
3504 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3505 {\r
3506   static Board lastReq[2], lastDrawn[2];\r
3507   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3508   static int lastDrawnFlipView = 0;\r
3509   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3510   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3511   HDC tmphdc;\r
3512   HDC hdcmem;\r
3513   HBITMAP bufferBitmap;\r
3514   HBITMAP oldBitmap;\r
3515   RECT Rect;\r
3516   HRGN clips[MAX_CLIPS];\r
3517   ChessSquare dragged_piece = EmptySquare;\r
3518   int nr = twoBoards*partnerUp;\r
3519 \r
3520   /* I'm undecided on this - this function figures out whether a full\r
3521    * repaint is necessary on its own, so there's no real reason to have the\r
3522    * caller tell it that.  I think this can safely be set to FALSE - but\r
3523    * if we trust the callers not to request full repaints unnessesarily, then\r
3524    * we could skip some clipping work.  In other words, only request a full\r
3525    * redraw when the majority of pieces have changed positions (ie. flip, \r
3526    * gamestart and similar)  --Hawk\r
3527    */\r
3528   Boolean fullrepaint = repaint;\r
3529 \r
3530   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3531 \r
3532   if( DrawPositionNeedsFullRepaint() ) {\r
3533       fullrepaint = TRUE;\r
3534   }\r
3535 \r
3536   if (board == NULL) {\r
3537     if (!lastReqValid[nr]) {\r
3538       return;\r
3539     }\r
3540     board = lastReq[nr];\r
3541   } else {\r
3542     CopyBoard(lastReq[nr], board);\r
3543     lastReqValid[nr] = 1;\r
3544   }\r
3545 \r
3546   if (doingSizing) {\r
3547     return;\r
3548   }\r
3549 \r
3550   if (IsIconic(hwndMain)) {\r
3551     return;\r
3552   }\r
3553 \r
3554   if (hdc == NULL) {\r
3555     hdc = GetDC(hwndMain);\r
3556     if (!appData.monoMode) {\r
3557       SelectPalette(hdc, hPal, FALSE);\r
3558       RealizePalette(hdc);\r
3559     }\r
3560     releaseDC = TRUE;\r
3561   } else {\r
3562     releaseDC = FALSE;\r
3563   }\r
3564 \r
3565   /* Create some work-DCs */\r
3566   hdcmem = CreateCompatibleDC(hdc);\r
3567   tmphdc = CreateCompatibleDC(hdc);\r
3568 \r
3569   /* If dragging is in progress, we temporarely remove the piece */\r
3570   /* [HGM] or temporarily decrease count if stacked              */\r
3571   /*       !! Moved to before board compare !!                   */\r
3572   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3573     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3574     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3575             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3576         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3577     } else \r
3578     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3579             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3580         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3581     } else \r
3582         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3583   }\r
3584 \r
3585   /* Figure out which squares need updating by comparing the \r
3586    * newest board with the last drawn board and checking if\r
3587    * flipping has changed.\r
3588    */\r
3589   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3590     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3591       for (column = 0; column < BOARD_WIDTH; column++) {\r
3592         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3593           SquareToPos(row, column, &x, &y);\r
3594           clips[num_clips++] =\r
3595             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3596         }\r
3597       }\r
3598     }\r
3599    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3600     for (i=0; i<2; i++) {\r
3601       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3602           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3603         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3604             lastDrawnHighlight.sq[i].y >= 0) {\r
3605           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3606                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3607           clips[num_clips++] =\r
3608             CreateRectRgn(x - lineGap, y - lineGap, \r
3609                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3610         }\r
3611         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3612           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3613           clips[num_clips++] =\r
3614             CreateRectRgn(x - lineGap, y - lineGap, \r
3615                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3616         }\r
3617       }\r
3618     }\r
3619     for (i=0; i<2; i++) {\r
3620       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3621           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3622         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3623             lastDrawnPremove.sq[i].y >= 0) {\r
3624           SquareToPos(lastDrawnPremove.sq[i].y,\r
3625                       lastDrawnPremove.sq[i].x, &x, &y);\r
3626           clips[num_clips++] =\r
3627             CreateRectRgn(x - lineGap, y - lineGap, \r
3628                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3629         }\r
3630         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3631             premoveHighlightInfo.sq[i].y >= 0) {\r
3632           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3633                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3634           clips[num_clips++] =\r
3635             CreateRectRgn(x - lineGap, y - lineGap, \r
3636                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3637         }\r
3638       }\r
3639     }\r
3640    } else { // nr == 1\r
3641         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3642         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3643         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3644         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3645       for (i=0; i<2; i++) {\r
3646         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3647             partnerHighlightInfo.sq[i].y >= 0) {\r
3648           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3649                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3650           clips[num_clips++] =\r
3651             CreateRectRgn(x - lineGap, y - lineGap, \r
3652                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3653         }\r
3654         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3655             oldPartnerHighlight.sq[i].y >= 0) {\r
3656           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3657                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3658           clips[num_clips++] =\r
3659             CreateRectRgn(x - lineGap, y - lineGap, \r
3660                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3661         }\r
3662       }\r
3663    }\r
3664   } else {\r
3665     fullrepaint = TRUE;\r
3666   }\r
3667 \r
3668   /* Create a buffer bitmap - this is the actual bitmap\r
3669    * being written to.  When all the work is done, we can\r
3670    * copy it to the real DC (the screen).  This avoids\r
3671    * the problems with flickering.\r
3672    */\r
3673   GetClientRect(hwndMain, &Rect);\r
3674   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3675                                         Rect.bottom-Rect.top+1);\r
3676   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3677   if (!appData.monoMode) {\r
3678     SelectPalette(hdcmem, hPal, FALSE);\r
3679   }\r
3680 \r
3681   /* Create clips for dragging */\r
3682   if (!fullrepaint) {\r
3683     if (dragInfo.from.x >= 0) {\r
3684       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3685       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3686     }\r
3687     if (dragInfo.start.x >= 0) {\r
3688       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3689       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3690     }\r
3691     if (dragInfo.pos.x >= 0) {\r
3692       x = dragInfo.pos.x - squareSize / 2;\r
3693       y = dragInfo.pos.y - squareSize / 2;\r
3694       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3695     }\r
3696     if (dragInfo.lastpos.x >= 0) {\r
3697       x = dragInfo.lastpos.x - squareSize / 2;\r
3698       y = dragInfo.lastpos.y - squareSize / 2;\r
3699       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3700     }\r
3701   }\r
3702 \r
3703   /* Are we animating a move?  \r
3704    * If so, \r
3705    *   - remove the piece from the board (temporarely)\r
3706    *   - calculate the clipping region\r
3707    */\r
3708   if (!fullrepaint) {\r
3709     if (animInfo.piece != EmptySquare) {\r
3710       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3711       x = boardRect.left + animInfo.lastpos.x;\r
3712       y = boardRect.top + animInfo.lastpos.y;\r
3713       x2 = boardRect.left + animInfo.pos.x;\r
3714       y2 = boardRect.top + animInfo.pos.y;\r
3715       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3716       /* Slight kludge.  The real problem is that after AnimateMove is\r
3717          done, the position on the screen does not match lastDrawn.\r
3718          This currently causes trouble only on e.p. captures in\r
3719          atomic, where the piece moves to an empty square and then\r
3720          explodes.  The old and new positions both had an empty square\r
3721          at the destination, but animation has drawn a piece there and\r
3722          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3723       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3724     }\r
3725   }\r
3726 \r
3727   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3728   if (num_clips == 0)\r
3729     fullrepaint = TRUE;\r
3730 \r
3731   /* Set clipping on the memory DC */\r
3732   if (!fullrepaint) {\r
3733     SelectClipRgn(hdcmem, clips[0]);\r
3734     for (x = 1; x < num_clips; x++) {\r
3735       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3736         abort();  // this should never ever happen!\r
3737     }\r
3738   }\r
3739 \r
3740   /* Do all the drawing to the memory DC */\r
3741   if(explodeInfo.radius) { // [HGM] atomic\r
3742         HBRUSH oldBrush;\r
3743         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3744         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3745         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3746         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3747         x += squareSize/2;\r
3748         y += squareSize/2;\r
3749         if(!fullrepaint) {\r
3750           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3751           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3752         }\r
3753         DrawGridOnDC(hdcmem);\r
3754         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3755         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3756         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3757         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3758         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3759         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3760         SelectObject(hdcmem, oldBrush);\r
3761   } else {\r
3762     DrawGridOnDC(hdcmem);\r
3763     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3764         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3765         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3766     } else {\r
3767         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3768         oldPartnerHighlight = partnerHighlightInfo;\r
3769     }\r
3770     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3771   }\r
3772   if(nr == 0) // [HGM] dual: markers only on left board\r
3773   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3774     for (column = 0; column < BOARD_WIDTH; column++) {\r
3775         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3776             HBRUSH oldBrush = SelectObject(hdcmem, \r
3777                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3778             SquareToPos(row, column, &x, &y);\r
3779             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3780                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3781             SelectObject(hdcmem, oldBrush);\r
3782         }\r
3783     }\r
3784   }\r
3785 \r
3786   if( appData.highlightMoveWithArrow ) {\r
3787     DrawArrowHighlight(hdcmem);\r
3788   }\r
3789 \r
3790   DrawCoordsOnDC(hdcmem);\r
3791 \r
3792   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3793                  /* to make sure lastDrawn contains what is actually drawn */\r
3794 \r
3795   /* Put the dragged piece back into place and draw it (out of place!) */\r
3796     if (dragged_piece != EmptySquare) {\r
3797     /* [HGM] or restack */\r
3798     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3799                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3800     else\r
3801     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3802                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3803     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3804     x = dragInfo.pos.x - squareSize / 2;\r
3805     y = dragInfo.pos.y - squareSize / 2;\r
3806     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3807                   ((int) dragInfo.piece < (int) BlackPawn), \r
3808                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3809   }   \r
3810   \r
3811   /* Put the animated piece back into place and draw it */\r
3812   if (animInfo.piece != EmptySquare) {\r
3813     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3814     x = boardRect.left + animInfo.pos.x;\r
3815     y = boardRect.top + animInfo.pos.y;\r
3816     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3817                   ((int) animInfo.piece < (int) BlackPawn),\r
3818                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3819   }\r
3820 \r
3821   /* Release the bufferBitmap by selecting in the old bitmap \r
3822    * and delete the memory DC\r
3823    */\r
3824   SelectObject(hdcmem, oldBitmap);\r
3825   DeleteDC(hdcmem);\r
3826 \r
3827   /* Set clipping on the target DC */\r
3828   if (!fullrepaint) {\r
3829     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3830         RECT rect;\r
3831         GetRgnBox(clips[x], &rect);\r
3832         DeleteObject(clips[x]);\r
3833         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3834                           rect.right + wpMain.width/2, rect.bottom);\r
3835     }\r
3836     SelectClipRgn(hdc, clips[0]);\r
3837     for (x = 1; x < num_clips; x++) {\r
3838       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3839         abort();   // this should never ever happen!\r
3840     } \r
3841   }\r
3842 \r
3843   /* Copy the new bitmap onto the screen in one go.\r
3844    * This way we avoid any flickering\r
3845    */\r
3846   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3847   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3848          boardRect.right - boardRect.left,\r
3849          boardRect.bottom - boardRect.top,\r
3850          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3851   if(saveDiagFlag) { \r
3852     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3853     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3854 \r
3855     GetObject(bufferBitmap, sizeof(b), &b);\r
3856     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3857         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3858         bih.biWidth = b.bmWidth;\r
3859         bih.biHeight = b.bmHeight;\r
3860         bih.biPlanes = 1;\r
3861         bih.biBitCount = b.bmBitsPixel;\r
3862         bih.biCompression = 0;\r
3863         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3864         bih.biXPelsPerMeter = 0;\r
3865         bih.biYPelsPerMeter = 0;\r
3866         bih.biClrUsed = 0;\r
3867         bih.biClrImportant = 0;\r
3868 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3869 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3870         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3871 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3872 \r
3873         wb = b.bmWidthBytes;\r
3874         // count colors\r
3875         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3876                 int k = ((int*) pData)[i];\r
3877                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3878                 if(j >= 16) break;\r
3879                 color[j] = k;\r
3880                 if(j >= nrColors) nrColors = j+1;\r
3881         }\r
3882         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3883                 INT p = 0;\r
3884                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3885                     for(w=0; w<(wb>>2); w+=2) {\r
3886                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3887                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3888                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3889                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3890                         pData[p++] = m | j<<4;\r
3891                     }\r
3892                     while(p&3) pData[p++] = 0;\r
3893                 }\r
3894                 fac = 3;\r
3895                 wb = ((wb+31)>>5)<<2;\r
3896         }\r
3897         // write BITMAPFILEHEADER\r
3898         fprintf(diagFile, "BM");\r
3899         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3900         fputDW(diagFile, 0);\r
3901         fputDW(diagFile, 0x36 + (fac?64:0));\r
3902         // write BITMAPINFOHEADER\r
3903         fputDW(diagFile, 40);\r
3904         fputDW(diagFile, b.bmWidth);\r
3905         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3906         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3907         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3908         fputDW(diagFile, 0);\r
3909         fputDW(diagFile, 0);\r
3910         fputDW(diagFile, 0);\r
3911         fputDW(diagFile, 0);\r
3912         fputDW(diagFile, 0);\r
3913         fputDW(diagFile, 0);\r
3914         // write color table\r
3915         if(fac)\r
3916         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3917         // write bitmap data\r
3918         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3919                 fputc(pData[i], diagFile);\r
3920         free(pData);\r
3921      }\r
3922   }\r
3923 \r
3924   SelectObject(tmphdc, oldBitmap);\r
3925 \r
3926   /* Massive cleanup */\r
3927   for (x = 0; x < num_clips; x++)\r
3928     DeleteObject(clips[x]);\r
3929 \r
3930   DeleteDC(tmphdc);\r
3931   DeleteObject(bufferBitmap);\r
3932 \r
3933   if (releaseDC) \r
3934     ReleaseDC(hwndMain, hdc);\r
3935   \r
3936   if (lastDrawnFlipView != flipView && nr == 0) {\r
3937     if (flipView)\r
3938       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3939     else\r
3940       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3941   }\r
3942 \r
3943 /*  CopyBoard(lastDrawn, board);*/\r
3944   lastDrawnHighlight = highlightInfo;\r
3945   lastDrawnPremove   = premoveHighlightInfo;\r
3946   lastDrawnFlipView = flipView;\r
3947   lastDrawnValid[nr] = 1;\r
3948 }\r
3949 \r
3950 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3951 int\r
3952 SaveDiagram(f)\r
3953      FILE *f;\r
3954 {\r
3955     saveDiagFlag = 1; diagFile = f;\r
3956     HDCDrawPosition(NULL, TRUE, NULL);\r
3957     saveDiagFlag = 0;\r
3958 \r
3959     fclose(f);\r
3960     return TRUE;\r
3961 }\r
3962 \r
3963 \r
3964 /*---------------------------------------------------------------------------*\\r
3965 | CLIENT PAINT PROCEDURE\r
3966 |   This is the main event-handler for the WM_PAINT message.\r
3967 |\r
3968 \*---------------------------------------------------------------------------*/\r
3969 VOID\r
3970 PaintProc(HWND hwnd)\r
3971 {\r
3972   HDC         hdc;\r
3973   PAINTSTRUCT ps;\r
3974   HFONT       oldFont;\r
3975 \r
3976   if((hdc = BeginPaint(hwnd, &ps))) {\r
3977     if (IsIconic(hwnd)) {\r
3978       DrawIcon(hdc, 2, 2, iconCurrent);\r
3979     } else {\r
3980       if (!appData.monoMode) {\r
3981         SelectPalette(hdc, hPal, FALSE);\r
3982         RealizePalette(hdc);\r
3983       }\r
3984       HDCDrawPosition(hdc, 1, NULL);\r
3985       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3986         flipView = !flipView; partnerUp = !partnerUp;\r
3987         HDCDrawPosition(hdc, 1, NULL);\r
3988         flipView = !flipView; partnerUp = !partnerUp;\r
3989       }\r
3990       oldFont =\r
3991         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3992       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3993                  ETO_CLIPPED|ETO_OPAQUE,\r
3994                  &messageRect, messageText, strlen(messageText), NULL);\r
3995       SelectObject(hdc, oldFont);\r
3996       DisplayBothClocks();\r
3997       DisplayLogos();\r
3998     }\r
3999     EndPaint(hwnd,&ps);\r
4000   }\r
4001 \r
4002   return;\r
4003 }\r
4004 \r
4005 \r
4006 /*\r
4007  * If the user selects on a border boundary, return -1; if off the board,\r
4008  *   return -2.  Otherwise map the event coordinate to the square.\r
4009  * The offset boardRect.left or boardRect.top must already have been\r
4010  *   subtracted from x.\r
4011  */\r
4012 int EventToSquare(x, limit)\r
4013      int x, limit;\r
4014 {\r
4015   if (x <= 0)\r
4016     return -2;\r
4017   if (x < lineGap)\r
4018     return -1;\r
4019   x -= lineGap;\r
4020   if ((x % (squareSize + lineGap)) >= squareSize)\r
4021     return -1;\r
4022   x /= (squareSize + lineGap);\r
4023     if (x >= limit)\r
4024     return -2;\r
4025   return x;\r
4026 }\r
4027 \r
4028 typedef struct {\r
4029   char piece;\r
4030   int command;\r
4031   char* name;\r
4032 } DropEnable;\r
4033 \r
4034 DropEnable dropEnables[] = {\r
4035   { 'P', DP_Pawn, N_("Pawn") },\r
4036   { 'N', DP_Knight, N_("Knight") },\r
4037   { 'B', DP_Bishop, N_("Bishop") },\r
4038   { 'R', DP_Rook, N_("Rook") },\r
4039   { 'Q', DP_Queen, N_("Queen") },\r
4040 };\r
4041 \r
4042 VOID\r
4043 SetupDropMenu(HMENU hmenu)\r
4044 {\r
4045   int i, count, enable;\r
4046   char *p;\r
4047   extern char white_holding[], black_holding[];\r
4048   char item[MSG_SIZ];\r
4049 \r
4050   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4051     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4052                dropEnables[i].piece);\r
4053     count = 0;\r
4054     while (p && *p++ == dropEnables[i].piece) count++;\r
4055       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4056     enable = count > 0 || !appData.testLegality\r
4057       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4058                       && !appData.icsActive);\r
4059     ModifyMenu(hmenu, dropEnables[i].command,\r
4060                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4061                dropEnables[i].command, item);\r
4062   }\r
4063 }\r
4064 \r
4065 void DragPieceBegin(int x, int y)\r
4066 {\r
4067       dragInfo.lastpos.x = boardRect.left + x;\r
4068       dragInfo.lastpos.y = boardRect.top + y;\r
4069       dragInfo.from.x = fromX;\r
4070       dragInfo.from.y = fromY;\r
4071       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4072       dragInfo.start = dragInfo.from;\r
4073       SetCapture(hwndMain);\r
4074 }\r
4075 \r
4076 void DragPieceEnd(int x, int y)\r
4077 {\r
4078     ReleaseCapture();\r
4079     dragInfo.start.x = dragInfo.start.y = -1;\r
4080     dragInfo.from = dragInfo.start;\r
4081     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4082 }\r
4083 \r
4084 void ChangeDragPiece(ChessSquare piece)\r
4085 {\r
4086     dragInfo.piece = piece;\r
4087 }\r
4088 \r
4089 /* Event handler for mouse messages */\r
4090 VOID\r
4091 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4092 {\r
4093   int x, y, menuNr;\r
4094   POINT pt;\r
4095   static int recursive = 0;\r
4096   HMENU hmenu;\r
4097   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4098 \r
4099   if (recursive) {\r
4100     if (message == WM_MBUTTONUP) {\r
4101       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4102          to the middle button: we simulate pressing the left button too!\r
4103          */\r
4104       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4105       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4106     }\r
4107     return;\r
4108   }\r
4109   recursive++;\r
4110   \r
4111   pt.x = LOWORD(lParam);\r
4112   pt.y = HIWORD(lParam);\r
4113   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4114   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4115   if (!flipView && y >= 0) {\r
4116     y = BOARD_HEIGHT - 1 - y;\r
4117   }\r
4118   if (flipView && x >= 0) {\r
4119     x = BOARD_WIDTH - 1 - x;\r
4120   }\r
4121 \r
4122   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4123 \r
4124   switch (message) {\r
4125   case WM_LBUTTONDOWN:\r
4126       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4127         ClockClick(flipClock);\r
4128       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4129         ClockClick(!flipClock);\r
4130       }\r
4131       dragInfo.start.x = dragInfo.start.y = -1;\r
4132       dragInfo.from = dragInfo.start;\r
4133     if(fromX == -1 && frozen) { // not sure where this is for\r
4134                 fromX = fromY = -1; \r
4135       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4136       break;\r
4137     }\r
4138       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4139       DrawPosition(TRUE, NULL);\r
4140     break;\r
4141 \r
4142   case WM_LBUTTONUP:\r
4143       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4144       DrawPosition(TRUE, NULL);\r
4145     break;\r
4146 \r
4147   case WM_MOUSEMOVE:\r
4148     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4149     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4150     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4151     if ((appData.animateDragging || appData.highlightDragging)\r
4152         && (wParam & MK_LBUTTON)\r
4153         && dragInfo.from.x >= 0) \r
4154     {\r
4155       BOOL full_repaint = FALSE;\r
4156 \r
4157       if (appData.animateDragging) {\r
4158         dragInfo.pos = pt;\r
4159       }\r
4160       if (appData.highlightDragging) {\r
4161         SetHighlights(fromX, fromY, x, y);\r
4162         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4163             full_repaint = TRUE;\r
4164         }\r
4165       }\r
4166       \r
4167       DrawPosition( full_repaint, NULL);\r
4168       \r
4169       dragInfo.lastpos = dragInfo.pos;\r
4170     }\r
4171     break;\r
4172 \r
4173   case WM_MOUSEWHEEL: // [DM]\r
4174     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4175        /* Mouse Wheel is being rolled forward\r
4176         * Play moves forward\r
4177         */\r
4178        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4179                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4180        /* Mouse Wheel is being rolled backward\r
4181         * Play moves backward\r
4182         */\r
4183        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4184                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4185     }\r
4186     break;\r
4187 \r
4188   case WM_MBUTTONUP:\r
4189   case WM_RBUTTONUP:\r
4190     ReleaseCapture();\r
4191     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4192     break;\r
4193  \r
4194   case WM_MBUTTONDOWN:\r
4195   case WM_RBUTTONDOWN:\r
4196     ErrorPopDown();\r
4197     ReleaseCapture();\r
4198     fromX = fromY = -1;\r
4199     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4200     dragInfo.start.x = dragInfo.start.y = -1;\r
4201     dragInfo.from = dragInfo.start;\r
4202     dragInfo.lastpos = dragInfo.pos;\r
4203     if (appData.highlightDragging) {\r
4204       ClearHighlights();\r
4205     }\r
4206     if(y == -2) {\r
4207       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4208       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4209           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4210       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4211           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4212       }\r
4213       break;\r
4214     }\r
4215     DrawPosition(TRUE, NULL);\r
4216 \r
4217     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4218     switch (menuNr) {\r
4219     case 0:\r
4220       if (message == WM_MBUTTONDOWN) {\r
4221         buttonCount = 3;  /* even if system didn't think so */\r
4222         if (wParam & MK_SHIFT) \r
4223           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4224         else\r
4225           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4226       } else { /* message == WM_RBUTTONDOWN */\r
4227         /* Just have one menu, on the right button.  Windows users don't\r
4228            think to try the middle one, and sometimes other software steals\r
4229            it, or it doesn't really exist. */\r
4230         if(gameInfo.variant != VariantShogi)\r
4231             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4232         else\r
4233             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4234       }\r
4235       break;\r
4236     case 2:\r
4237       SetCapture(hwndMain);
4238       break;\r
4239     case 1:\r
4240       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4241       SetupDropMenu(hmenu);\r
4242       MenuPopup(hwnd, pt, hmenu, -1);\r
4243     default:\r
4244       break;\r
4245     }\r
4246     break;\r
4247   }\r
4248 \r
4249   recursive--;\r
4250 }\r
4251 \r
4252 /* Preprocess messages for buttons in main window */\r
4253 LRESULT CALLBACK\r
4254 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4255 {\r
4256   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4257   int i, dir;\r
4258 \r
4259   for (i=0; i<N_BUTTONS; i++) {\r
4260     if (buttonDesc[i].id == id) break;\r
4261   }\r
4262   if (i == N_BUTTONS) return 0;\r
4263   switch (message) {\r
4264   case WM_KEYDOWN:\r
4265     switch (wParam) {\r
4266     case VK_LEFT:\r
4267     case VK_RIGHT:\r
4268       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4269       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4270       return TRUE;\r
4271     }\r
4272     break;\r
4273   case WM_CHAR:\r
4274     switch (wParam) {\r
4275     case '\r':\r
4276       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4277       return TRUE;\r
4278     default:\r
4279       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4280         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4281         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4282         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4283         SetFocus(h);\r
4284         SendMessage(h, WM_CHAR, wParam, lParam);\r
4285         return TRUE;\r
4286       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4287         TypeInEvent((char)wParam);\r
4288       }\r
4289       break;\r
4290     }\r
4291     break;\r
4292   }\r
4293   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4294 }\r
4295 \r
4296 /* Process messages for Promotion dialog box */\r
4297 LRESULT CALLBACK\r
4298 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4299 {\r
4300   char promoChar;\r
4301 \r
4302   switch (message) {\r
4303   case WM_INITDIALOG: /* message: initialize dialog box */\r
4304     /* Center the dialog over the application window */\r
4305     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4306     Translate(hDlg, DLG_PromotionKing);\r
4307     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4308       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4309        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4310        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4311                SW_SHOW : SW_HIDE);\r
4312     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4313     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4314        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4315          PieceToChar(WhiteAngel) != '~') ||\r
4316         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4317          PieceToChar(BlackAngel) != '~')   ) ?\r
4318                SW_SHOW : SW_HIDE);\r
4319     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4320        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4321          PieceToChar(WhiteMarshall) != '~') ||\r
4322         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4323          PieceToChar(BlackMarshall) != '~')   ) ?\r
4324                SW_SHOW : SW_HIDE);\r
4325     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4326     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4327        gameInfo.variant != VariantShogi ?\r
4328                SW_SHOW : SW_HIDE);\r
4329     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4330        gameInfo.variant != VariantShogi ?\r
4331                SW_SHOW : SW_HIDE);\r
4332     if(gameInfo.variant == VariantShogi) {\r
4333         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4334         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4335         SetWindowText(hDlg, "Promote?");\r
4336     }\r
4337     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4338        gameInfo.variant == VariantSuper ?\r
4339                SW_SHOW : SW_HIDE);\r
4340     return TRUE;\r
4341 \r
4342   case WM_COMMAND: /* message: received a command */\r
4343     switch (LOWORD(wParam)) {\r
4344     case IDCANCEL:\r
4345       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4346       ClearHighlights();\r
4347       DrawPosition(FALSE, NULL);\r
4348       return TRUE;\r
4349     case PB_King:\r
4350       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4351       break;\r
4352     case PB_Queen:\r
4353       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4354       break;\r
4355     case PB_Rook:\r
4356       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4357       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4358       break;\r
4359     case PB_Bishop:\r
4360       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4361       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4362       break;\r
4363     case PB_Chancellor:\r
4364       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4365       break;\r
4366     case PB_Archbishop:\r
4367       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4368       break;\r
4369     case PB_Knight:\r
4370       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4371       break;\r
4372     default:\r
4373       return FALSE;\r
4374     }\r
4375     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4376     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4377     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4378     fromX = fromY = -1;\r
4379     if (!appData.highlightLastMove) {\r
4380       ClearHighlights();\r
4381       DrawPosition(FALSE, NULL);\r
4382     }\r
4383     return TRUE;\r
4384   }\r
4385   return FALSE;\r
4386 }\r
4387 \r
4388 /* Pop up promotion dialog */\r
4389 VOID\r
4390 PromotionPopup(HWND hwnd)\r
4391 {\r
4392   FARPROC lpProc;\r
4393 \r
4394   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4395   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4396     hwnd, (DLGPROC)lpProc);\r
4397   FreeProcInstance(lpProc);\r
4398 }\r
4399 \r
4400 void\r
4401 PromotionPopUp()\r
4402 {\r
4403   DrawPosition(TRUE, NULL);\r
4404   PromotionPopup(hwndMain);\r
4405 }\r
4406 \r
4407 /* Toggle ShowThinking */\r
4408 VOID\r
4409 ToggleShowThinking()\r
4410 {\r
4411   appData.showThinking = !appData.showThinking;\r
4412   ShowThinkingEvent();\r
4413 }\r
4414 \r
4415 VOID\r
4416 LoadGameDialog(HWND hwnd, char* title)\r
4417 {\r
4418   UINT number = 0;\r
4419   FILE *f;\r
4420   char fileTitle[MSG_SIZ];\r
4421   f = OpenFileDialog(hwnd, "rb", "",\r
4422                      appData.oldSaveStyle ? "gam" : "pgn",\r
4423                      GAME_FILT,\r
4424                      title, &number, fileTitle, NULL);\r
4425   if (f != NULL) {\r
4426     cmailMsgLoaded = FALSE;\r
4427     if (number == 0) {\r
4428       int error = GameListBuild(f);\r
4429       if (error) {\r
4430         DisplayError(_("Cannot build game list"), error);\r
4431       } else if (!ListEmpty(&gameList) &&\r
4432                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4433         GameListPopUp(f, fileTitle);\r
4434         return;\r
4435       }\r
4436       GameListDestroy();\r
4437       number = 1;\r
4438     }\r
4439     LoadGame(f, number, fileTitle, FALSE);\r
4440   }\r
4441 }\r
4442 \r
4443 int get_term_width()\r
4444 {\r
4445     HDC hdc;\r
4446     TEXTMETRIC tm;\r
4447     RECT rc;\r
4448     HFONT hfont, hold_font;\r
4449     LOGFONT lf;\r
4450     HWND hText;\r
4451 \r
4452     if (hwndConsole)\r
4453         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4454     else\r
4455         return 79;\r
4456 \r
4457     // get the text metrics\r
4458     hdc = GetDC(hText);\r
4459     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4460     if (consoleCF.dwEffects & CFE_BOLD)\r
4461         lf.lfWeight = FW_BOLD;\r
4462     if (consoleCF.dwEffects & CFE_ITALIC)\r
4463         lf.lfItalic = TRUE;\r
4464     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4465         lf.lfStrikeOut = TRUE;\r
4466     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4467         lf.lfUnderline = TRUE;\r
4468     hfont = CreateFontIndirect(&lf);\r
4469     hold_font = SelectObject(hdc, hfont);\r
4470     GetTextMetrics(hdc, &tm);\r
4471     SelectObject(hdc, hold_font);\r
4472     DeleteObject(hfont);\r
4473     ReleaseDC(hText, hdc);\r
4474 \r
4475     // get the rectangle\r
4476     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4477 \r
4478     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4479 }\r
4480 \r
4481 void UpdateICSWidth(HWND hText)\r
4482 {\r
4483     LONG old_width, new_width;\r
4484 \r
4485     new_width = get_term_width(hText, FALSE);\r
4486     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4487     if (new_width != old_width)\r
4488     {\r
4489         ics_update_width(new_width);\r
4490         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4491     }\r
4492 }\r
4493 \r
4494 VOID\r
4495 ChangedConsoleFont()\r
4496 {\r
4497   CHARFORMAT cfmt;\r
4498   CHARRANGE tmpsel, sel;\r
4499   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4500   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4501   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4502   PARAFORMAT paraf;\r
4503 \r
4504   cfmt.cbSize = sizeof(CHARFORMAT);\r
4505   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4506     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4507                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4508   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4509    * size.  This was undocumented in the version of MSVC++ that I had\r
4510    * when I wrote the code, but is apparently documented now.\r
4511    */\r
4512   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4513   cfmt.bCharSet = f->lf.lfCharSet;\r
4514   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4515   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4516   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4517   /* Why are the following seemingly needed too? */\r
4518   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4519   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4520   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4521   tmpsel.cpMin = 0;\r
4522   tmpsel.cpMax = -1; /*999999?*/\r
4523   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4524   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4525   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4526    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4527    */\r
4528   paraf.cbSize = sizeof(paraf);\r
4529   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4530   paraf.dxStartIndent = 0;\r
4531   paraf.dxOffset = WRAP_INDENT;\r
4532   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4533   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4534   UpdateICSWidth(hText);\r
4535 }\r
4536 \r
4537 /*---------------------------------------------------------------------------*\\r
4538  *\r
4539  * Window Proc for main window\r
4540  *\r
4541 \*---------------------------------------------------------------------------*/\r
4542 \r
4543 /* Process messages for main window, etc. */\r
4544 LRESULT CALLBACK\r
4545 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4546 {\r
4547   FARPROC lpProc;\r
4548   int wmId, wmEvent;\r
4549   char *defName;\r
4550   FILE *f;\r
4551   UINT number;\r
4552   char fileTitle[MSG_SIZ];\r
4553   char buf[MSG_SIZ];\r
4554   static SnapData sd;\r
4555 \r
4556   switch (message) {\r
4557 \r
4558   case WM_PAINT: /* message: repaint portion of window */\r
4559     PaintProc(hwnd);\r
4560     break;\r
4561 \r
4562   case WM_ERASEBKGND:\r
4563     if (IsIconic(hwnd)) {\r
4564       /* Cheat; change the message */\r
4565       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4566     } else {\r
4567       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4568     }\r
4569     break;\r
4570 \r
4571   case WM_LBUTTONDOWN:\r
4572   case WM_MBUTTONDOWN:\r
4573   case WM_RBUTTONDOWN:\r
4574   case WM_LBUTTONUP:\r
4575   case WM_MBUTTONUP:\r
4576   case WM_RBUTTONUP:\r
4577   case WM_MOUSEMOVE:\r
4578   case WM_MOUSEWHEEL:\r
4579     MouseEvent(hwnd, message, wParam, lParam);\r
4580     break;\r
4581 \r
4582   JAWS_KB_NAVIGATION\r
4583 \r
4584   case WM_CHAR:\r
4585     \r
4586     JAWS_ALT_INTERCEPT\r
4587 \r
4588     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4589         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4590         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4591         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4592         SetFocus(h);\r
4593         SendMessage(h, message, wParam, lParam);\r
4594     } else if(lParam != KF_REPEAT) {\r
4595         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4596                 TypeInEvent((char)wParam);\r
4597         } else if((char)wParam == 003) CopyGameToClipboard();\r
4598          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4599     }\r
4600 \r
4601     break;\r
4602 \r
4603   case WM_PALETTECHANGED:\r
4604     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4605       int nnew;\r
4606       HDC hdc = GetDC(hwndMain);\r
4607       SelectPalette(hdc, hPal, TRUE);\r
4608       nnew = RealizePalette(hdc);\r
4609       if (nnew > 0) {\r
4610         paletteChanged = TRUE;\r
4611         InvalidateRect(hwnd, &boardRect, FALSE);\r
4612       }\r
4613       ReleaseDC(hwnd, hdc);\r
4614     }\r
4615     break;\r
4616 \r
4617   case WM_QUERYNEWPALETTE:\r
4618     if (!appData.monoMode /*&& paletteChanged*/) {\r
4619       int nnew;\r
4620       HDC hdc = GetDC(hwndMain);\r
4621       paletteChanged = FALSE;\r
4622       SelectPalette(hdc, hPal, FALSE);\r
4623       nnew = RealizePalette(hdc);\r
4624       if (nnew > 0) {\r
4625         InvalidateRect(hwnd, &boardRect, FALSE);\r
4626       }\r
4627       ReleaseDC(hwnd, hdc);\r
4628       return TRUE;\r
4629     }\r
4630     return FALSE;\r
4631 \r
4632   case WM_COMMAND: /* message: command from application menu */\r
4633     wmId    = LOWORD(wParam);\r
4634     wmEvent = HIWORD(wParam);\r
4635 \r
4636     switch (wmId) {\r
4637     case IDM_NewGame:\r
4638       ResetGameEvent();\r
4639       SAY("new game enter a move to play against the computer with white");\r
4640       break;\r
4641 \r
4642     case IDM_NewGameFRC:\r
4643       if( NewGameFRC() == 0 ) {\r
4644         ResetGameEvent();\r
4645       }\r
4646       break;\r
4647 \r
4648     case IDM_NewVariant:\r
4649       NewVariantPopup(hwnd);\r
4650       break;\r
4651 \r
4652     case IDM_LoadGame:\r
4653       LoadGameDialog(hwnd, _("Load Game from File"));\r
4654       break;\r
4655 \r
4656     case IDM_LoadNextGame:\r
4657       ReloadGame(1);\r
4658       break;\r
4659 \r
4660     case IDM_LoadPrevGame:\r
4661       ReloadGame(-1);\r
4662       break;\r
4663 \r
4664     case IDM_ReloadGame:\r
4665       ReloadGame(0);\r
4666       break;\r
4667 \r
4668     case IDM_LoadPosition:\r
4669       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4670         Reset(FALSE, TRUE);\r
4671       }\r
4672       number = 1;\r
4673       f = OpenFileDialog(hwnd, "rb", "",\r
4674                          appData.oldSaveStyle ? "pos" : "fen",\r
4675                          POSITION_FILT,\r
4676                          _("Load Position from File"), &number, fileTitle, NULL);\r
4677       if (f != NULL) {\r
4678         LoadPosition(f, number, fileTitle);\r
4679       }\r
4680       break;\r
4681 \r
4682     case IDM_LoadNextPosition:\r
4683       ReloadPosition(1);\r
4684       break;\r
4685 \r
4686     case IDM_LoadPrevPosition:\r
4687       ReloadPosition(-1);\r
4688       break;\r
4689 \r
4690     case IDM_ReloadPosition:\r
4691       ReloadPosition(0);\r
4692       break;\r
4693 \r
4694     case IDM_SaveGame:\r
4695       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4696       f = OpenFileDialog(hwnd, "a", defName,\r
4697                          appData.oldSaveStyle ? "gam" : "pgn",\r
4698                          GAME_FILT,\r
4699                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4700       if (f != NULL) {\r
4701         SaveGame(f, 0, "");\r
4702       }\r
4703       break;\r
4704 \r
4705     case IDM_SavePosition:\r
4706       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4707       f = OpenFileDialog(hwnd, "a", defName,\r
4708                          appData.oldSaveStyle ? "pos" : "fen",\r
4709                          POSITION_FILT,\r
4710                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4711       if (f != NULL) {\r
4712         SavePosition(f, 0, "");\r
4713       }\r
4714       break;\r
4715 \r
4716     case IDM_SaveDiagram:\r
4717       defName = "diagram";\r
4718       f = OpenFileDialog(hwnd, "wb", defName,\r
4719                          "bmp",\r
4720                          DIAGRAM_FILT,\r
4721                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4722       if (f != NULL) {\r
4723         SaveDiagram(f);\r
4724       }\r
4725       break;\r
4726 \r
4727     case IDM_CopyGame:\r
4728       CopyGameToClipboard();\r
4729       break;\r
4730 \r
4731     case IDM_PasteGame:\r
4732       PasteGameFromClipboard();\r
4733       break;\r
4734 \r
4735     case IDM_CopyGameListToClipboard:\r
4736       CopyGameListToClipboard();\r
4737       break;\r
4738 \r
4739     /* [AS] Autodetect FEN or PGN data */\r
4740     case IDM_PasteAny:\r
4741       PasteGameOrFENFromClipboard();\r
4742       break;\r
4743 \r
4744     /* [AS] Move history */\r
4745     case IDM_ShowMoveHistory:\r
4746         if( MoveHistoryIsUp() ) {\r
4747             MoveHistoryPopDown();\r
4748         }\r
4749         else {\r
4750             MoveHistoryPopUp();\r
4751         }\r
4752         break;\r
4753 \r
4754     /* [AS] Eval graph */\r
4755     case IDM_ShowEvalGraph:\r
4756         if( EvalGraphIsUp() ) {\r
4757             EvalGraphPopDown();\r
4758         }\r
4759         else {\r
4760             EvalGraphPopUp();\r
4761             SetFocus(hwndMain);\r
4762         }\r
4763         break;\r
4764 \r
4765     /* [AS] Engine output */\r
4766     case IDM_ShowEngineOutput:\r
4767         if( EngineOutputIsUp() ) {\r
4768             EngineOutputPopDown();\r
4769         }\r
4770         else {\r
4771             EngineOutputPopUp();\r
4772         }\r
4773         break;\r
4774 \r
4775     /* [AS] User adjudication */\r
4776     case IDM_UserAdjudication_White:\r
4777         UserAdjudicationEvent( +1 );\r
4778         break;\r
4779 \r
4780     case IDM_UserAdjudication_Black:\r
4781         UserAdjudicationEvent( -1 );\r
4782         break;\r
4783 \r
4784     case IDM_UserAdjudication_Draw:\r
4785         UserAdjudicationEvent( 0 );\r
4786         break;\r
4787 \r
4788     /* [AS] Game list options dialog */\r
4789     case IDM_GameListOptions:\r
4790       GameListOptions();\r
4791       break;\r
4792 \r
4793     case IDM_NewChat:\r
4794       ChatPopUp(NULL);\r
4795       break;\r
4796 \r
4797     case IDM_CopyPosition:\r
4798       CopyFENToClipboard();\r
4799       break;\r
4800 \r
4801     case IDM_PastePosition:\r
4802       PasteFENFromClipboard();\r
4803       break;\r
4804 \r
4805     case IDM_MailMove:\r
4806       MailMoveEvent();\r
4807       break;\r
4808 \r
4809     case IDM_ReloadCMailMsg:\r
4810       Reset(TRUE, TRUE);\r
4811       ReloadCmailMsgEvent(FALSE);\r
4812       break;\r
4813 \r
4814     case IDM_Minimize:\r
4815       ShowWindow(hwnd, SW_MINIMIZE);\r
4816       break;\r
4817 \r
4818     case IDM_Exit:\r
4819       ExitEvent(0);\r
4820       break;\r
4821 \r
4822     case IDM_MachineWhite:\r
4823       MachineWhiteEvent();\r
4824       /*\r
4825        * refresh the tags dialog only if it's visible\r
4826        */\r
4827       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4828           char *tags;\r
4829           tags = PGNTags(&gameInfo);\r
4830           TagsPopUp(tags, CmailMsg());\r
4831           free(tags);\r
4832       }\r
4833       SAY("computer starts playing white");\r
4834       break;\r
4835 \r
4836     case IDM_MachineBlack:\r
4837       MachineBlackEvent();\r
4838       /*\r
4839        * refresh the tags dialog only if it's visible\r
4840        */\r
4841       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4842           char *tags;\r
4843           tags = PGNTags(&gameInfo);\r
4844           TagsPopUp(tags, CmailMsg());\r
4845           free(tags);\r
4846       }\r
4847       SAY("computer starts playing black");\r
4848       break;\r
4849 \r
4850     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4851       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4852       break;\r
4853 \r
4854     case IDM_TwoMachines:\r
4855       TwoMachinesEvent();\r
4856       /*\r
4857        * refresh the tags dialog only if it's visible\r
4858        */\r
4859       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4860           char *tags;\r
4861           tags = PGNTags(&gameInfo);\r
4862           TagsPopUp(tags, CmailMsg());\r
4863           free(tags);\r
4864       }\r
4865       SAY("computer starts playing both sides");\r
4866       break;\r
4867 \r
4868     case IDM_AnalysisMode:\r
4869       if (!first.analysisSupport) {\r
4870         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4871         DisplayError(buf, 0);\r
4872       } else {\r
4873         SAY("analyzing current position");\r
4874         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4875         if (appData.icsActive) {\r
4876                if (gameMode != IcsObserving) {\r
4877                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4878                        DisplayError(buf, 0);\r
4879                        /* secure check */\r
4880                        if (appData.icsEngineAnalyze) {\r
4881                                if (appData.debugMode) \r
4882                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4883                                ExitAnalyzeMode();\r
4884                                ModeHighlight();\r
4885                                break;\r
4886                        }\r
4887                        break;\r
4888                } else {\r
4889                        /* if enable, user want disable icsEngineAnalyze */\r
4890                        if (appData.icsEngineAnalyze) {\r
4891                                ExitAnalyzeMode();\r
4892                                ModeHighlight();\r
4893                                break;\r
4894                        }\r
4895                        appData.icsEngineAnalyze = TRUE;\r
4896                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4897                }\r
4898         } \r
4899         if (!appData.showThinking) ToggleShowThinking();\r
4900         AnalyzeModeEvent();\r
4901       }\r
4902       break;\r
4903 \r
4904     case IDM_AnalyzeFile:\r
4905       if (!first.analysisSupport) {\r
4906         char buf[MSG_SIZ];\r
4907           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4908         DisplayError(buf, 0);\r
4909       } else {\r
4910         if (!appData.showThinking) ToggleShowThinking();\r
4911         AnalyzeFileEvent();\r
4912         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4913         AnalysisPeriodicEvent(1);\r
4914       }\r
4915       break;\r
4916 \r
4917     case IDM_IcsClient:\r
4918       IcsClientEvent();\r
4919       break;\r
4920 \r
4921     case IDM_EditGame:\r
4922     case IDM_EditGame2:\r
4923       EditGameEvent();\r
4924       SAY("edit game");\r
4925       break;\r
4926 \r
4927     case IDM_EditPosition:\r
4928     case IDM_EditPosition2:\r
4929       EditPositionEvent();\r
4930       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4931       break;\r
4932 \r
4933     case IDM_Training:\r
4934       TrainingEvent();\r
4935       break;\r
4936 \r
4937     case IDM_ShowGameList:\r
4938       ShowGameListProc();\r
4939       break;\r
4940 \r
4941     case IDM_EditProgs1:\r
4942       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4943       break;\r
4944 \r
4945     case IDM_EditProgs2:\r
4946      LoadEnginePopUp(hwndMain);\r
4947 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4948       break;\r
4949 \r
4950     case IDM_EditServers:\r
4951       EditTagsPopUp(icsNames, &icsNames);\r
4952       break;\r
4953 \r
4954     case IDM_EditTags:\r
4955     case IDM_Tags:\r
4956       EditTagsProc();\r
4957       break;\r
4958 \r
4959     case IDM_EditBook:\r
4960       EditBookEvent();\r
4961       break;\r
4962 \r
4963     case IDM_EditComment:\r
4964     case IDM_Comment:\r
4965       if (commentUp && editComment) {\r
4966         CommentPopDown();\r
4967       } else {\r
4968         EditCommentEvent();\r
4969       }\r
4970       break;\r
4971 \r
4972     case IDM_Pause:\r
4973       PauseEvent();\r
4974       break;\r
4975 \r
4976     case IDM_Accept:\r
4977       AcceptEvent();\r
4978       break;\r
4979 \r
4980     case IDM_Decline:\r
4981       DeclineEvent();\r
4982       break;\r
4983 \r
4984     case IDM_Rematch:\r
4985       RematchEvent();\r
4986       break;\r
4987 \r
4988     case IDM_CallFlag:\r
4989       CallFlagEvent();\r
4990       break;\r
4991 \r
4992     case IDM_Draw:\r
4993       DrawEvent();\r
4994       break;\r
4995 \r
4996     case IDM_Adjourn:\r
4997       AdjournEvent();\r
4998       break;\r
4999 \r
5000     case IDM_Abort:\r
5001       AbortEvent();\r
5002       break;\r
5003 \r
5004     case IDM_Resign:\r
5005       ResignEvent();\r
5006       break;\r
5007 \r
5008     case IDM_StopObserving:\r
5009       StopObservingEvent();\r
5010       break;\r
5011 \r
5012     case IDM_StopExamining:\r
5013       StopExaminingEvent();\r
5014       break;\r
5015 \r
5016     case IDM_Upload:\r
5017       UploadGameEvent();\r
5018       break;\r
5019 \r
5020     case IDM_TypeInMove:\r
5021       TypeInEvent('\000');\r
5022       break;\r
5023 \r
5024     case IDM_TypeInName:\r
5025       PopUpNameDialog('\000');\r
5026       break;\r
5027 \r
5028     case IDM_Backward:\r
5029       BackwardEvent();\r
5030       SetFocus(hwndMain);\r
5031       break;\r
5032 \r
5033     JAWS_MENU_ITEMS\r
5034 \r
5035     case IDM_Forward:\r
5036       ForwardEvent();\r
5037       SetFocus(hwndMain);\r
5038       break;\r
5039 \r
5040     case IDM_ToStart:\r
5041       ToStartEvent();\r
5042       SetFocus(hwndMain);\r
5043       break;\r
5044 \r
5045     case IDM_ToEnd:\r
5046       ToEndEvent();\r
5047       SetFocus(hwndMain);\r
5048       break;\r
5049 \r
5050     case IDM_Revert:\r
5051       RevertEvent(FALSE);\r
5052       break;\r
5053 \r
5054     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5055       RevertEvent(TRUE);\r
5056       break;\r
5057 \r
5058     case IDM_TruncateGame:\r
5059       TruncateGameEvent();\r
5060       break;\r
5061 \r
5062     case IDM_MoveNow:\r
5063       MoveNowEvent();\r
5064       break;\r
5065 \r
5066     case IDM_RetractMove:\r
5067       RetractMoveEvent();\r
5068       break;\r
5069 \r
5070     case IDM_FlipView:\r
5071       flipView = !flipView;\r
5072       DrawPosition(FALSE, NULL);\r
5073       break;\r
5074 \r
5075     case IDM_FlipClock:\r
5076       flipClock = !flipClock;\r
5077       DisplayBothClocks();\r
5078       DisplayLogos();\r
5079       break;\r
5080 \r
5081     case IDM_MuteSounds:\r
5082       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5083       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5084                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5085       break;\r
5086 \r
5087     case IDM_GeneralOptions:\r
5088       GeneralOptionsPopup(hwnd);\r
5089       DrawPosition(TRUE, NULL);\r
5090       break;\r
5091 \r
5092     case IDM_BoardOptions:\r
5093       BoardOptionsPopup(hwnd);\r
5094       break;\r
5095 \r
5096     case IDM_EnginePlayOptions:\r
5097       EnginePlayOptionsPopup(hwnd);\r
5098       break;\r
5099 \r
5100     case IDM_Engine1Options:\r
5101       EngineOptionsPopup(hwnd, &first);\r
5102       break;\r
5103 \r
5104     case IDM_Engine2Options:\r
5105       savedHwnd = hwnd;\r
5106       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5107       EngineOptionsPopup(hwnd, &second);\r
5108       break;\r
5109 \r
5110     case IDM_OptionsUCI:\r
5111       UciOptionsPopup(hwnd);\r
5112       break;\r
5113 \r
5114     case IDM_Tourney:\r
5115       TourneyPopup(hwnd);\r
5116       break;\r
5117 \r
5118     case IDM_IcsOptions:\r
5119       IcsOptionsPopup(hwnd);\r
5120       break;\r
5121 \r
5122     case IDM_Fonts:\r
5123       FontsOptionsPopup(hwnd);\r
5124       break;\r
5125 \r
5126     case IDM_Sounds:\r
5127       SoundOptionsPopup(hwnd);\r
5128       break;\r
5129 \r
5130     case IDM_CommPort:\r
5131       CommPortOptionsPopup(hwnd);\r
5132       break;\r
5133 \r
5134     case IDM_LoadOptions:\r
5135       LoadOptionsPopup(hwnd);\r
5136       break;\r
5137 \r
5138     case IDM_SaveOptions:\r
5139       SaveOptionsPopup(hwnd);\r
5140       break;\r
5141 \r
5142     case IDM_TimeControl:\r
5143       TimeControlOptionsPopup(hwnd);\r
5144       break;\r
5145 \r
5146     case IDM_SaveSettings:\r
5147       SaveSettings(settingsFileName);\r
5148       break;\r
5149 \r
5150     case IDM_SaveSettingsOnExit:\r
5151       saveSettingsOnExit = !saveSettingsOnExit;\r
5152       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5153                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5154                                          MF_CHECKED : MF_UNCHECKED));\r
5155       break;\r
5156 \r
5157     case IDM_Hint:\r
5158       HintEvent();\r
5159       break;\r
5160 \r
5161     case IDM_Book:\r
5162       BookEvent();\r
5163       break;\r
5164 \r
5165     case IDM_AboutGame:\r
5166       AboutGameEvent();\r
5167       break;\r
5168 \r
5169     case IDM_Debug:\r
5170       appData.debugMode = !appData.debugMode;\r
5171       if (appData.debugMode) {\r
5172         char dir[MSG_SIZ];\r
5173         GetCurrentDirectory(MSG_SIZ, dir);\r
5174         SetCurrentDirectory(installDir);\r
5175         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5176         SetCurrentDirectory(dir);\r
5177         setbuf(debugFP, NULL);\r
5178       } else {\r
5179         fclose(debugFP);\r
5180         debugFP = NULL;\r
5181       }\r
5182       break;\r
5183 \r
5184     case IDM_HELPCONTENTS:\r
5185       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5186           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5187           MessageBox (GetFocus(),\r
5188                     _("Unable to activate help"),\r
5189                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5190       }\r
5191       break;\r
5192 \r
5193     case IDM_HELPSEARCH:\r
5194         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5195             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5196         MessageBox (GetFocus(),\r
5197                     _("Unable to activate help"),\r
5198                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5199       }\r
5200       break;\r
5201 \r
5202     case IDM_HELPHELP:\r
5203       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5204         MessageBox (GetFocus(),\r
5205                     _("Unable to activate help"),\r
5206                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5207       }\r
5208       break;\r
5209 \r
5210     case IDM_ABOUT:\r
5211       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5212       DialogBox(hInst, \r
5213         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5214         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5215       FreeProcInstance(lpProc);\r
5216       break;\r
5217 \r
5218     case IDM_DirectCommand1:\r
5219       AskQuestionEvent(_("Direct Command"),\r
5220                        _("Send to chess program:"), "", "1");\r
5221       break;\r
5222     case IDM_DirectCommand2:\r
5223       AskQuestionEvent(_("Direct Command"),\r
5224                        _("Send to second chess program:"), "", "2");\r
5225       break;\r
5226 \r
5227     case EP_WhitePawn:\r
5228       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5229       fromX = fromY = -1;\r
5230       break;\r
5231 \r
5232     case EP_WhiteKnight:\r
5233       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5234       fromX = fromY = -1;\r
5235       break;\r
5236 \r
5237     case EP_WhiteBishop:\r
5238       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5239       fromX = fromY = -1;\r
5240       break;\r
5241 \r
5242     case EP_WhiteRook:\r
5243       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5244       fromX = fromY = -1;\r
5245       break;\r
5246 \r
5247     case EP_WhiteQueen:\r
5248       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5249       fromX = fromY = -1;\r
5250       break;\r
5251 \r
5252     case EP_WhiteFerz:\r
5253       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5254       fromX = fromY = -1;\r
5255       break;\r
5256 \r
5257     case EP_WhiteWazir:\r
5258       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5259       fromX = fromY = -1;\r
5260       break;\r
5261 \r
5262     case EP_WhiteAlfil:\r
5263       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5264       fromX = fromY = -1;\r
5265       break;\r
5266 \r
5267     case EP_WhiteCannon:\r
5268       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5269       fromX = fromY = -1;\r
5270       break;\r
5271 \r
5272     case EP_WhiteCardinal:\r
5273       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5274       fromX = fromY = -1;\r
5275       break;\r
5276 \r
5277     case EP_WhiteMarshall:\r
5278       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5279       fromX = fromY = -1;\r
5280       break;\r
5281 \r
5282     case EP_WhiteKing:\r
5283       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5284       fromX = fromY = -1;\r
5285       break;\r
5286 \r
5287     case EP_BlackPawn:\r
5288       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5289       fromX = fromY = -1;\r
5290       break;\r
5291 \r
5292     case EP_BlackKnight:\r
5293       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5294       fromX = fromY = -1;\r
5295       break;\r
5296 \r
5297     case EP_BlackBishop:\r
5298       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5299       fromX = fromY = -1;\r
5300       break;\r
5301 \r
5302     case EP_BlackRook:\r
5303       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5304       fromX = fromY = -1;\r
5305       break;\r
5306 \r
5307     case EP_BlackQueen:\r
5308       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5309       fromX = fromY = -1;\r
5310       break;\r
5311 \r
5312     case EP_BlackFerz:\r
5313       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5314       fromX = fromY = -1;\r
5315       break;\r
5316 \r
5317     case EP_BlackWazir:\r
5318       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5319       fromX = fromY = -1;\r
5320       break;\r
5321 \r
5322     case EP_BlackAlfil:\r
5323       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5324       fromX = fromY = -1;\r
5325       break;\r
5326 \r
5327     case EP_BlackCannon:\r
5328       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5329       fromX = fromY = -1;\r
5330       break;\r
5331 \r
5332     case EP_BlackCardinal:\r
5333       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5334       fromX = fromY = -1;\r
5335       break;\r
5336 \r
5337     case EP_BlackMarshall:\r
5338       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5339       fromX = fromY = -1;\r
5340       break;\r
5341 \r
5342     case EP_BlackKing:\r
5343       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5344       fromX = fromY = -1;\r
5345       break;\r
5346 \r
5347     case EP_EmptySquare:\r
5348       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5349       fromX = fromY = -1;\r
5350       break;\r
5351 \r
5352     case EP_ClearBoard:\r
5353       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5354       fromX = fromY = -1;\r
5355       break;\r
5356 \r
5357     case EP_White:\r
5358       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5359       fromX = fromY = -1;\r
5360       break;\r
5361 \r
5362     case EP_Black:\r
5363       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5364       fromX = fromY = -1;\r
5365       break;\r
5366 \r
5367     case EP_Promote:\r
5368       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5369       fromX = fromY = -1;\r
5370       break;\r
5371 \r
5372     case EP_Demote:\r
5373       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5374       fromX = fromY = -1;\r
5375       break;\r
5376 \r
5377     case DP_Pawn:\r
5378       DropMenuEvent(WhitePawn, fromX, fromY);\r
5379       fromX = fromY = -1;\r
5380       break;\r
5381 \r
5382     case DP_Knight:\r
5383       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5384       fromX = fromY = -1;\r
5385       break;\r
5386 \r
5387     case DP_Bishop:\r
5388       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5389       fromX = fromY = -1;\r
5390       break;\r
5391 \r
5392     case DP_Rook:\r
5393       DropMenuEvent(WhiteRook, fromX, fromY);\r
5394       fromX = fromY = -1;\r
5395       break;\r
5396 \r
5397     case DP_Queen:\r
5398       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5399       fromX = fromY = -1;\r
5400       break;\r
5401 \r
5402     case IDM_English:\r
5403       barbaric = 0; appData.language = "";\r
5404       TranslateMenus(0);\r
5405       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5406       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5407       lastChecked = wmId;\r
5408       break;\r
5409 \r
5410     default:\r
5411       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5412           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5413           TranslateMenus(0);\r
5414           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5415           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5416           lastChecked = wmId;\r
5417           break;\r
5418       }\r
5419       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5420     }\r
5421     break;\r
5422 \r
5423   case WM_TIMER:\r
5424     switch (wParam) {\r
5425     case CLOCK_TIMER_ID:\r
5426       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5427       clockTimerEvent = 0;\r
5428       DecrementClocks(); /* call into back end */\r
5429       break;\r
5430     case LOAD_GAME_TIMER_ID:\r
5431       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5432       loadGameTimerEvent = 0;\r
5433       AutoPlayGameLoop(); /* call into back end */\r
5434       break;\r
5435     case ANALYSIS_TIMER_ID:\r
5436       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5437                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5438         AnalysisPeriodicEvent(0);\r
5439       } else {\r
5440         KillTimer(hwnd, analysisTimerEvent);\r
5441         analysisTimerEvent = 0;\r
5442       }\r
5443       break;\r
5444     case DELAYED_TIMER_ID:\r
5445       KillTimer(hwnd, delayedTimerEvent);\r
5446       delayedTimerEvent = 0;\r
5447       delayedTimerCallback();\r
5448       break;\r
5449     }\r
5450     break;\r
5451 \r
5452   case WM_USER_Input:\r
5453     InputEvent(hwnd, message, wParam, lParam);\r
5454     break;\r
5455 \r
5456   /* [AS] Also move "attached" child windows */\r
5457   case WM_WINDOWPOSCHANGING:\r
5458 \r
5459     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5460         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5461 \r
5462         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5463             /* Window is moving */\r
5464             RECT rcMain;\r
5465 \r
5466 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5467             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5468             rcMain.right  = wpMain.x + wpMain.width;\r
5469             rcMain.top    = wpMain.y;\r
5470             rcMain.bottom = wpMain.y + wpMain.height;\r
5471             \r
5472             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5473             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5474             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5475             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5476             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5477             wpMain.x = lpwp->x;\r
5478             wpMain.y = lpwp->y;\r
5479         }\r
5480     }\r
5481     break;\r
5482 \r
5483   /* [AS] Snapping */\r
5484   case WM_ENTERSIZEMOVE:\r
5485     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5486     if (hwnd == hwndMain) {\r
5487       doingSizing = TRUE;\r
5488       lastSizing = 0;\r
5489     }\r
5490     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5491     break;\r
5492 \r
5493   case WM_SIZING:\r
5494     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5495     if (hwnd == hwndMain) {\r
5496       lastSizing = wParam;\r
5497     }\r
5498     break;\r
5499 \r
5500   case WM_MOVING:\r
5501     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5502       return OnMoving( &sd, hwnd, wParam, lParam );\r
5503 \r
5504   case WM_EXITSIZEMOVE:\r
5505     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5506     if (hwnd == hwndMain) {\r
5507       RECT client;\r
5508       doingSizing = FALSE;\r
5509       InvalidateRect(hwnd, &boardRect, FALSE);\r
5510       GetClientRect(hwnd, &client);\r
5511       ResizeBoard(client.right, client.bottom, lastSizing);\r
5512       lastSizing = 0;\r
5513       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5514     }\r
5515     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5516     break;\r
5517 \r
5518   case WM_DESTROY: /* message: window being destroyed */\r
5519     PostQuitMessage(0);\r
5520     break;\r
5521 \r
5522   case WM_CLOSE:\r
5523     if (hwnd == hwndMain) {\r
5524       ExitEvent(0);\r
5525     }\r
5526     break;\r
5527 \r
5528   default:      /* Passes it on if unprocessed */\r
5529     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5530   }\r
5531   return 0;\r
5532 }\r
5533 \r
5534 /*---------------------------------------------------------------------------*\\r
5535  *\r
5536  * Misc utility routines\r
5537  *\r
5538 \*---------------------------------------------------------------------------*/\r
5539 \r
5540 /*\r
5541  * Decent random number generator, at least not as bad as Windows\r
5542  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5543  */\r
5544 unsigned int randstate;\r
5545 \r
5546 int\r
5547 myrandom(void)\r
5548 {\r
5549   randstate = randstate * 1664525 + 1013904223;\r
5550   return (int) randstate & 0x7fffffff;\r
5551 }\r
5552 \r
5553 void\r
5554 mysrandom(unsigned int seed)\r
5555 {\r
5556   randstate = seed;\r
5557 }\r
5558 \r
5559 \r
5560 /* \r
5561  * returns TRUE if user selects a different color, FALSE otherwise \r
5562  */\r
5563 \r
5564 BOOL\r
5565 ChangeColor(HWND hwnd, COLORREF *which)\r
5566 {\r
5567   static BOOL firstTime = TRUE;\r
5568   static DWORD customColors[16];\r
5569   CHOOSECOLOR cc;\r
5570   COLORREF newcolor;\r
5571   int i;\r
5572   ColorClass ccl;\r
5573 \r
5574   if (firstTime) {\r
5575     /* Make initial colors in use available as custom colors */\r
5576     /* Should we put the compiled-in defaults here instead? */\r
5577     i = 0;\r
5578     customColors[i++] = lightSquareColor & 0xffffff;\r
5579     customColors[i++] = darkSquareColor & 0xffffff;\r
5580     customColors[i++] = whitePieceColor & 0xffffff;\r
5581     customColors[i++] = blackPieceColor & 0xffffff;\r
5582     customColors[i++] = highlightSquareColor & 0xffffff;\r
5583     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5584 \r
5585     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5586       customColors[i++] = textAttribs[ccl].color;\r
5587     }\r
5588     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5589     firstTime = FALSE;\r
5590   }\r
5591 \r
5592   cc.lStructSize = sizeof(cc);\r
5593   cc.hwndOwner = hwnd;\r
5594   cc.hInstance = NULL;\r
5595   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5596   cc.lpCustColors = (LPDWORD) customColors;\r
5597   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5598 \r
5599   if (!ChooseColor(&cc)) return FALSE;\r
5600 \r
5601   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5602   if (newcolor == *which) return FALSE;\r
5603   *which = newcolor;\r
5604   return TRUE;\r
5605 \r
5606   /*\r
5607   InitDrawingColors();\r
5608   InvalidateRect(hwnd, &boardRect, FALSE);\r
5609   */\r
5610 }\r
5611 \r
5612 BOOLEAN\r
5613 MyLoadSound(MySound *ms)\r
5614 {\r
5615   BOOL ok = FALSE;\r
5616   struct stat st;\r
5617   FILE *f;\r
5618 \r
5619   if (ms->data) free(ms->data);\r
5620   ms->data = NULL;\r
5621 \r
5622   switch (ms->name[0]) {\r
5623   case NULLCHAR:\r
5624     /* Silence */\r
5625     ok = TRUE;\r
5626     break;\r
5627   case '$':\r
5628     /* System sound from Control Panel.  Don't preload here. */\r
5629     ok = TRUE;\r
5630     break;\r
5631   case '!':\r
5632     if (ms->name[1] == NULLCHAR) {\r
5633       /* "!" alone = silence */\r
5634       ok = TRUE;\r
5635     } else {\r
5636       /* Builtin wave resource.  Error if not found. */\r
5637       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5638       if (h == NULL) break;\r
5639       ms->data = (void *)LoadResource(hInst, h);\r
5640       if (h == NULL) break;\r
5641       ok = TRUE;\r
5642     }\r
5643     break;\r
5644   default:\r
5645     /* .wav file.  Error if not found. */\r
5646     f = fopen(ms->name, "rb");\r
5647     if (f == NULL) break;\r
5648     if (fstat(fileno(f), &st) < 0) break;\r
5649     ms->data = malloc(st.st_size);\r
5650     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5651     fclose(f);\r
5652     ok = TRUE;\r
5653     break;\r
5654   }\r
5655   if (!ok) {\r
5656     char buf[MSG_SIZ];\r
5657       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5658     DisplayError(buf, GetLastError());\r
5659   }\r
5660   return ok;\r
5661 }\r
5662 \r
5663 BOOLEAN\r
5664 MyPlaySound(MySound *ms)\r
5665 {\r
5666   BOOLEAN ok = FALSE;\r
5667 \r
5668   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5669   switch (ms->name[0]) {\r
5670   case NULLCHAR:\r
5671         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5672     /* Silence */\r
5673     ok = TRUE;\r
5674     break;\r
5675   case '$':\r
5676     /* System sound from Control Panel (deprecated feature).\r
5677        "$" alone or an unset sound name gets default beep (still in use). */\r
5678     if (ms->name[1]) {\r
5679       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5680     }\r
5681     if (!ok) ok = MessageBeep(MB_OK);\r
5682     break; \r
5683   case '!':\r
5684     /* Builtin wave resource, or "!" alone for silence */\r
5685     if (ms->name[1]) {\r
5686       if (ms->data == NULL) return FALSE;\r
5687       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5688     } else {\r
5689       ok = TRUE;\r
5690     }\r
5691     break;\r
5692   default:\r
5693     /* .wav file.  Error if not found. */\r
5694     if (ms->data == NULL) return FALSE;\r
5695     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5696     break;\r
5697   }\r
5698   /* Don't print an error: this can happen innocently if the sound driver\r
5699      is busy; for instance, if another instance of WinBoard is playing\r
5700      a sound at about the same time. */\r
5701   return ok;\r
5702 }\r
5703 \r
5704 \r
5705 LRESULT CALLBACK\r
5706 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5707 {\r
5708   BOOL ok;\r
5709   OPENFILENAME *ofn;\r
5710   static UINT *number; /* gross that this is static */\r
5711 \r
5712   switch (message) {\r
5713   case WM_INITDIALOG: /* message: initialize dialog box */\r
5714     /* Center the dialog over the application window */\r
5715     ofn = (OPENFILENAME *) lParam;\r
5716     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5717       number = (UINT *) ofn->lCustData;\r
5718       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5719     } else {\r
5720       number = NULL;\r
5721     }\r
5722     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5723     Translate(hDlg, 1536);\r
5724     return FALSE;  /* Allow for further processing */\r
5725 \r
5726   case WM_COMMAND:\r
5727     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5728       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5729     }\r
5730     return FALSE;  /* Allow for further processing */\r
5731   }\r
5732   return FALSE;\r
5733 }\r
5734 \r
5735 UINT APIENTRY\r
5736 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5737 {\r
5738   static UINT *number;\r
5739   OPENFILENAME *ofname;\r
5740   OFNOTIFY *ofnot;\r
5741   switch (uiMsg) {\r
5742   case WM_INITDIALOG:\r
5743     Translate(hdlg, DLG_IndexNumber);\r
5744     ofname = (OPENFILENAME *)lParam;\r
5745     number = (UINT *)(ofname->lCustData);\r
5746     break;\r
5747   case WM_NOTIFY:\r
5748     ofnot = (OFNOTIFY *)lParam;\r
5749     if (ofnot->hdr.code == CDN_FILEOK) {\r
5750       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5751     }\r
5752     break;\r
5753   }\r
5754   return 0;\r
5755 }\r
5756 \r
5757 \r
5758 FILE *\r
5759 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5760                char *nameFilt, char *dlgTitle, UINT *number,\r
5761                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5762 {\r
5763   OPENFILENAME openFileName;\r
5764   char buf1[MSG_SIZ];\r
5765   FILE *f;\r
5766 \r
5767   if (fileName == NULL) fileName = buf1;\r
5768   if (defName == NULL) {\r
5769     safeStrCpy(fileName, "*.", 3 );\r
5770     strcat(fileName, defExt);\r
5771   } else {\r
5772     safeStrCpy(fileName, defName, MSG_SIZ );\r
5773   }\r
5774     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5775   if (number) *number = 0;\r
5776 \r
5777   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5778   openFileName.hwndOwner         = hwnd;\r
5779   openFileName.hInstance         = (HANDLE) hInst;\r
5780   openFileName.lpstrFilter       = nameFilt;\r
5781   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5782   openFileName.nMaxCustFilter    = 0L;\r
5783   openFileName.nFilterIndex      = 1L;\r
5784   openFileName.lpstrFile         = fileName;\r
5785   openFileName.nMaxFile          = MSG_SIZ;\r
5786   openFileName.lpstrFileTitle    = fileTitle;\r
5787   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5788   openFileName.lpstrInitialDir   = NULL;\r
5789   openFileName.lpstrTitle        = dlgTitle;\r
5790   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5791     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5792     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5793     | (oldDialog ? 0 : OFN_EXPLORER);\r
5794   openFileName.nFileOffset       = 0;\r
5795   openFileName.nFileExtension    = 0;\r
5796   openFileName.lpstrDefExt       = defExt;\r
5797   openFileName.lCustData         = (LONG) number;\r
5798   openFileName.lpfnHook          = oldDialog ?\r
5799     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5800   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5801 \r
5802   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5803                         GetOpenFileName(&openFileName)) {\r
5804     /* open the file */\r
5805     f = fopen(openFileName.lpstrFile, write);\r
5806     if (f == NULL) {\r
5807       MessageBox(hwnd, _("File open failed"), NULL,\r
5808                  MB_OK|MB_ICONEXCLAMATION);\r
5809       return NULL;\r
5810     }\r
5811   } else {\r
5812     int err = CommDlgExtendedError();\r
5813     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5814     return FALSE;\r
5815   }\r
5816   return f;\r
5817 }\r
5818 \r
5819 \r
5820 \r
5821 VOID APIENTRY\r
5822 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5823 {\r
5824   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5825 \r
5826   /*\r
5827    * Get the first pop-up menu in the menu template. This is the\r
5828    * menu that TrackPopupMenu displays.\r
5829    */\r
5830   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5831   TranslateOneMenu(10, hmenuTrackPopup);\r
5832 \r
5833   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5834 \r
5835   /*\r
5836    * TrackPopup uses screen coordinates, so convert the\r
5837    * coordinates of the mouse click to screen coordinates.\r
5838    */\r
5839   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5840 \r
5841   /* Draw and track the floating pop-up menu. */\r
5842   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5843                  pt.x, pt.y, 0, hwnd, NULL);\r
5844 \r
5845   /* Destroy the menu.*/\r
5846   DestroyMenu(hmenu);\r
5847 }\r
5848    \r
5849 typedef struct {\r
5850   HWND hDlg, hText;\r
5851   int sizeX, sizeY, newSizeX, newSizeY;\r
5852   HDWP hdwp;\r
5853 } ResizeEditPlusButtonsClosure;\r
5854 \r
5855 BOOL CALLBACK\r
5856 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5857 {\r
5858   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5859   RECT rect;\r
5860   POINT pt;\r
5861 \r
5862   if (hChild == cl->hText) return TRUE;\r
5863   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5864   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5865   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5866   ScreenToClient(cl->hDlg, &pt);\r
5867   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5868     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5869   return TRUE;\r
5870 }\r
5871 \r
5872 /* Resize a dialog that has a (rich) edit field filling most of\r
5873    the top, with a row of buttons below */\r
5874 VOID\r
5875 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5876 {\r
5877   RECT rectText;\r
5878   int newTextHeight, newTextWidth;\r
5879   ResizeEditPlusButtonsClosure cl;\r
5880   \r
5881   /*if (IsIconic(hDlg)) return;*/\r
5882   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5883   \r
5884   cl.hdwp = BeginDeferWindowPos(8);\r
5885 \r
5886   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5887   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5888   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5889   if (newTextHeight < 0) {\r
5890     newSizeY += -newTextHeight;\r
5891     newTextHeight = 0;\r
5892   }\r
5893   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5894     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5895 \r
5896   cl.hDlg = hDlg;\r
5897   cl.hText = hText;\r
5898   cl.sizeX = sizeX;\r
5899   cl.sizeY = sizeY;\r
5900   cl.newSizeX = newSizeX;\r
5901   cl.newSizeY = newSizeY;\r
5902   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5903 \r
5904   EndDeferWindowPos(cl.hdwp);\r
5905 }\r
5906 \r
5907 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5908 {\r
5909     RECT    rChild, rParent;\r
5910     int     wChild, hChild, wParent, hParent;\r
5911     int     wScreen, hScreen, xNew, yNew;\r
5912     HDC     hdc;\r
5913 \r
5914     /* Get the Height and Width of the child window */\r
5915     GetWindowRect (hwndChild, &rChild);\r
5916     wChild = rChild.right - rChild.left;\r
5917     hChild = rChild.bottom - rChild.top;\r
5918 \r
5919     /* Get the Height and Width of the parent window */\r
5920     GetWindowRect (hwndParent, &rParent);\r
5921     wParent = rParent.right - rParent.left;\r
5922     hParent = rParent.bottom - rParent.top;\r
5923 \r
5924     /* Get the display limits */\r
5925     hdc = GetDC (hwndChild);\r
5926     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5927     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5928     ReleaseDC(hwndChild, hdc);\r
5929 \r
5930     /* Calculate new X position, then adjust for screen */\r
5931     xNew = rParent.left + ((wParent - wChild) /2);\r
5932     if (xNew < 0) {\r
5933         xNew = 0;\r
5934     } else if ((xNew+wChild) > wScreen) {\r
5935         xNew = wScreen - wChild;\r
5936     }\r
5937 \r
5938     /* Calculate new Y position, then adjust for screen */\r
5939     if( mode == 0 ) {\r
5940         yNew = rParent.top  + ((hParent - hChild) /2);\r
5941     }\r
5942     else {\r
5943         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5944     }\r
5945 \r
5946     if (yNew < 0) {\r
5947         yNew = 0;\r
5948     } else if ((yNew+hChild) > hScreen) {\r
5949         yNew = hScreen - hChild;\r
5950     }\r
5951 \r
5952     /* Set it, and return */\r
5953     return SetWindowPos (hwndChild, NULL,\r
5954                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5955 }\r
5956 \r
5957 /* Center one window over another */\r
5958 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5959 {\r
5960     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5961 }\r
5962 \r
5963 /*---------------------------------------------------------------------------*\\r
5964  *\r
5965  * Startup Dialog functions\r
5966  *\r
5967 \*---------------------------------------------------------------------------*/\r
5968 void\r
5969 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5970 {\r
5971   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5972 \r
5973   while (*cd != NULL) {\r
5974     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5975     cd++;\r
5976   }\r
5977 }\r
5978 \r
5979 void\r
5980 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5981 {\r
5982   char buf1[MAX_ARG_LEN];\r
5983   int len;\r
5984 \r
5985   if (str[0] == '@') {\r
5986     FILE* f = fopen(str + 1, "r");\r
5987     if (f == NULL) {\r
5988       DisplayFatalError(str + 1, errno, 2);\r
5989       return;\r
5990     }\r
5991     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5992     fclose(f);\r
5993     buf1[len] = NULLCHAR;\r
5994     str = buf1;\r
5995   }\r
5996 \r
5997   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5998 \r
5999   for (;;) {\r
6000     char buf[MSG_SIZ];\r
6001     char *end = strchr(str, '\n');\r
6002     if (end == NULL) return;\r
6003     memcpy(buf, str, end - str);\r
6004     buf[end - str] = NULLCHAR;\r
6005     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6006     str = end + 1;\r
6007   }\r
6008 }\r
6009 \r
6010 void\r
6011 SetStartupDialogEnables(HWND hDlg)\r
6012 {\r
6013   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6014     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6015     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6016   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6017     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6018   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6019     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6020   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6021     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6022   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6023     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6024     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6025     IsDlgButtonChecked(hDlg, OPT_View));\r
6026 }\r
6027 \r
6028 char *\r
6029 QuoteForFilename(char *filename)\r
6030 {\r
6031   int dquote, space;\r
6032   dquote = strchr(filename, '"') != NULL;\r
6033   space = strchr(filename, ' ') != NULL;\r
6034   if (dquote || space) {\r
6035     if (dquote) {\r
6036       return "'";\r
6037     } else {\r
6038       return "\"";\r
6039     }\r
6040   } else {\r
6041     return "";\r
6042   }\r
6043 }\r
6044 \r
6045 VOID\r
6046 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6047 {\r
6048   char buf[MSG_SIZ];\r
6049   char *q;\r
6050 \r
6051   InitComboStringsFromOption(hwndCombo, nthnames);\r
6052   q = QuoteForFilename(nthcp);\r
6053     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6054   if (*nthdir != NULLCHAR) {\r
6055     q = QuoteForFilename(nthdir);\r
6056       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6057   }\r
6058   if (*nthcp == NULLCHAR) {\r
6059     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6060   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6061     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6062     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6063   }\r
6064 }\r
6065 \r
6066 LRESULT CALLBACK\r
6067 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6068 {\r
6069   char buf[MSG_SIZ];\r
6070   HANDLE hwndCombo;\r
6071   char *p;\r
6072 \r
6073   switch (message) {\r
6074   case WM_INITDIALOG:\r
6075     /* Center the dialog */\r
6076     CenterWindow (hDlg, GetDesktopWindow());\r
6077     Translate(hDlg, DLG_Startup);\r
6078     /* Initialize the dialog items */\r
6079     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6080                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6081                   firstChessProgramNames);\r
6082     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6083                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6084                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6085     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6086     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6087       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6088     if (*appData.icsHelper != NULLCHAR) {\r
6089       char *q = QuoteForFilename(appData.icsHelper);\r
6090       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6091     }\r
6092     if (*appData.icsHost == NULLCHAR) {\r
6093       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6094       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6095     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6096       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6097       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6098     }\r
6099 \r
6100     if (appData.icsActive) {\r
6101       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6102     }\r
6103     else if (appData.noChessProgram) {\r
6104       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6105     }\r
6106     else {\r
6107       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6108     }\r
6109 \r
6110     SetStartupDialogEnables(hDlg);\r
6111     return TRUE;\r
6112 \r
6113   case WM_COMMAND:\r
6114     switch (LOWORD(wParam)) {\r
6115     case IDOK:\r
6116       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6117         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6118         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6119         p = buf;\r
6120         ParseArgs(StringGet, &p);\r
6121         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6122         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6123         p = buf;
6124         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6125         ParseArgs(StringGet, &p);\r
6126         SwapEngines(singleList); // ... and then make it 'second'\r
6127         appData.noChessProgram = FALSE;\r
6128         appData.icsActive = FALSE;\r
6129       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6130         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6131         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6132         p = buf;\r
6133         ParseArgs(StringGet, &p);\r
6134         if (appData.zippyPlay) {\r
6135           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6136           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6137           p = buf;\r
6138           ParseArgs(StringGet, &p);\r
6139         }\r
6140       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6141         appData.noChessProgram = TRUE;\r
6142         appData.icsActive = FALSE;\r
6143       } else {\r
6144         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6145                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6146         return TRUE;\r
6147       }\r
6148       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6149         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6150         p = buf;\r
6151         ParseArgs(StringGet, &p);\r
6152       }\r
6153       EndDialog(hDlg, TRUE);\r
6154       return TRUE;\r
6155 \r
6156     case IDCANCEL:\r
6157       ExitEvent(0);\r
6158       return TRUE;\r
6159 \r
6160     case IDM_HELPCONTENTS:\r
6161       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6162         MessageBox (GetFocus(),\r
6163                     _("Unable to activate help"),\r
6164                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6165       }\r
6166       break;\r
6167 \r
6168     default:\r
6169       SetStartupDialogEnables(hDlg);\r
6170       break;\r
6171     }\r
6172     break;\r
6173   }\r
6174   return FALSE;\r
6175 }\r
6176 \r
6177 /*---------------------------------------------------------------------------*\\r
6178  *\r
6179  * About box dialog functions\r
6180  *\r
6181 \*---------------------------------------------------------------------------*/\r
6182 \r
6183 /* Process messages for "About" dialog box */\r
6184 LRESULT CALLBACK\r
6185 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6186 {\r
6187   switch (message) {\r
6188   case WM_INITDIALOG: /* message: initialize dialog box */\r
6189     /* Center the dialog over the application window */\r
6190     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6191     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6192     Translate(hDlg, ABOUTBOX);\r
6193     JAWS_COPYRIGHT\r
6194     return (TRUE);\r
6195 \r
6196   case WM_COMMAND: /* message: received a command */\r
6197     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6198         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6199       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6200       return (TRUE);\r
6201     }\r
6202     break;\r
6203   }\r
6204   return (FALSE);\r
6205 }\r
6206 \r
6207 /*---------------------------------------------------------------------------*\\r
6208  *\r
6209  * Comment Dialog functions\r
6210  *\r
6211 \*---------------------------------------------------------------------------*/\r
6212 \r
6213 LRESULT CALLBACK\r
6214 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6215 {\r
6216   static HANDLE hwndText = NULL;\r
6217   int len, newSizeX, newSizeY, flags;\r
6218   static int sizeX, sizeY;\r
6219   char *str;\r
6220   RECT rect;\r
6221   MINMAXINFO *mmi;\r
6222 \r
6223   switch (message) {\r
6224   case WM_INITDIALOG: /* message: initialize dialog box */\r
6225     /* Initialize the dialog items */\r
6226     Translate(hDlg, DLG_EditComment);\r
6227     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6228     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6229     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6230     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6231     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6232     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6233     SetWindowText(hDlg, commentTitle);\r
6234     if (editComment) {\r
6235       SetFocus(hwndText);\r
6236     } else {\r
6237       SetFocus(GetDlgItem(hDlg, IDOK));\r
6238     }\r
6239     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6240                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6241                 MAKELPARAM(FALSE, 0));\r
6242     /* Size and position the dialog */\r
6243     if (!commentDialog) {\r
6244       commentDialog = hDlg;\r
6245       flags = SWP_NOZORDER;\r
6246       GetClientRect(hDlg, &rect);\r
6247       sizeX = rect.right;\r
6248       sizeY = rect.bottom;\r
6249       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6250           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6251         WINDOWPLACEMENT wp;\r
6252         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6253         wp.length = sizeof(WINDOWPLACEMENT);\r
6254         wp.flags = 0;\r
6255         wp.showCmd = SW_SHOW;\r
6256         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6257         wp.rcNormalPosition.left = wpComment.x;\r
6258         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6259         wp.rcNormalPosition.top = wpComment.y;\r
6260         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6261         SetWindowPlacement(hDlg, &wp);\r
6262 \r
6263         GetClientRect(hDlg, &rect);\r
6264         newSizeX = rect.right;\r
6265         newSizeY = rect.bottom;\r
6266         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6267                               newSizeX, newSizeY);\r
6268         sizeX = newSizeX;\r
6269         sizeY = newSizeY;\r
6270       }\r
6271     }\r
6272     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6273     return FALSE;\r
6274 \r
6275   case WM_COMMAND: /* message: received a command */\r
6276     switch (LOWORD(wParam)) {\r
6277     case IDOK:\r
6278       if (editComment) {\r
6279         char *p, *q;\r
6280         /* Read changed options from the dialog box */\r
6281         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6282         len = GetWindowTextLength(hwndText);\r
6283         str = (char *) malloc(len + 1);\r
6284         GetWindowText(hwndText, str, len + 1);\r
6285         p = q = str;\r
6286         while (*q) {\r
6287           if (*q == '\r')\r
6288             q++;\r
6289           else\r
6290             *p++ = *q++;\r
6291         }\r
6292         *p = NULLCHAR;\r
6293         ReplaceComment(commentIndex, str);\r
6294         free(str);\r
6295       }\r
6296       CommentPopDown();\r
6297       return TRUE;\r
6298 \r
6299     case IDCANCEL:\r
6300     case OPT_CancelComment:\r
6301       CommentPopDown();\r
6302       return TRUE;\r
6303 \r
6304     case OPT_ClearComment:\r
6305       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6306       break;\r
6307 \r
6308     case OPT_EditComment:\r
6309       EditCommentEvent();\r
6310       return TRUE;\r
6311 \r
6312     default:\r
6313       break;\r
6314     }\r
6315     break;\r
6316 \r
6317   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6318         if( wParam == OPT_CommentText ) {\r
6319             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6320 \r
6321             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6322                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6323                 POINTL pt;\r
6324                 LRESULT index;\r
6325 \r
6326                 pt.x = LOWORD( lpMF->lParam );\r
6327                 pt.y = HIWORD( lpMF->lParam );\r
6328 \r
6329                 if(lpMF->msg == WM_CHAR) {\r
6330                         CHARRANGE sel;\r
6331                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6332                         index = sel.cpMin;\r
6333                 } else\r
6334                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6335 \r
6336                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6337                 len = GetWindowTextLength(hwndText);\r
6338                 str = (char *) malloc(len + 1);\r
6339                 GetWindowText(hwndText, str, len + 1);\r
6340                 ReplaceComment(commentIndex, str);\r
6341                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6342                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6343                 free(str);\r
6344 \r
6345                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6346                 lpMF->msg = WM_USER;\r
6347 \r
6348                 return TRUE;\r
6349             }\r
6350         }\r
6351         break;\r
6352 \r
6353   case WM_SIZE:\r
6354     newSizeX = LOWORD(lParam);\r
6355     newSizeY = HIWORD(lParam);\r
6356     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6357     sizeX = newSizeX;\r
6358     sizeY = newSizeY;\r
6359     break;\r
6360 \r
6361   case WM_GETMINMAXINFO:\r
6362     /* Prevent resizing window too small */\r
6363     mmi = (MINMAXINFO *) lParam;\r
6364     mmi->ptMinTrackSize.x = 100;\r
6365     mmi->ptMinTrackSize.y = 100;\r
6366     break;\r
6367   }\r
6368   return FALSE;\r
6369 }\r
6370 \r
6371 VOID\r
6372 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6373 {\r
6374   FARPROC lpProc;\r
6375   char *p, *q;\r
6376 \r
6377   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6378 \r
6379   if (str == NULL) str = "";\r
6380   p = (char *) malloc(2 * strlen(str) + 2);\r
6381   q = p;\r
6382   while (*str) {\r
6383     if (*str == '\n') *q++ = '\r';\r
6384     *q++ = *str++;\r
6385   }\r
6386   *q = NULLCHAR;\r
6387   if (commentText != NULL) free(commentText);\r
6388 \r
6389   commentIndex = index;\r
6390   commentTitle = title;\r
6391   commentText = p;\r
6392   editComment = edit;\r
6393 \r
6394   if (commentDialog) {\r
6395     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6396     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6397   } else {\r
6398     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6399     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6400                  hwndMain, (DLGPROC)lpProc);\r
6401     FreeProcInstance(lpProc);\r
6402   }\r
6403   commentUp = TRUE;\r
6404 }\r
6405 \r
6406 \r
6407 /*---------------------------------------------------------------------------*\\r
6408  *\r
6409  * Type-in move dialog functions\r
6410  * \r
6411 \*---------------------------------------------------------------------------*/\r
6412 \r
6413 LRESULT CALLBACK\r
6414 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6415 {\r
6416   char move[MSG_SIZ];\r
6417   HWND hInput;\r
6418 \r
6419   switch (message) {\r
6420   case WM_INITDIALOG:\r
6421     move[0] = (char) lParam;\r
6422     move[1] = NULLCHAR;\r
6423     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6424     Translate(hDlg, DLG_TypeInMove);\r
6425     hInput = GetDlgItem(hDlg, OPT_Move);\r
6426     SetWindowText(hInput, move);\r
6427     SetFocus(hInput);\r
6428     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6429     return FALSE;\r
6430 \r
6431   case WM_COMMAND:\r
6432     switch (LOWORD(wParam)) {\r
6433     case IDOK:
6434 \r
6435       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6436       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6437       TypeInDoneEvent(move);\r
6438       EndDialog(hDlg, TRUE);\r
6439       return TRUE;\r
6440     case IDCANCEL:\r
6441       EndDialog(hDlg, FALSE);\r
6442       return TRUE;\r
6443     default:\r
6444       break;\r
6445     }\r
6446     break;\r
6447   }\r
6448   return FALSE;\r
6449 }\r
6450 \r
6451 VOID\r
6452 PopUpMoveDialog(char firstchar)\r
6453 {\r
6454     FARPROC lpProc;\r
6455 \r
6456       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6457       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6458         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6459       FreeProcInstance(lpProc);\r
6460 }\r
6461 \r
6462 /*---------------------------------------------------------------------------*\\r
6463  *\r
6464  * Type-in name dialog functions\r
6465  * \r
6466 \*---------------------------------------------------------------------------*/\r
6467 \r
6468 LRESULT CALLBACK\r
6469 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6470 {\r
6471   char move[MSG_SIZ];\r
6472   HWND hInput;\r
6473 \r
6474   switch (message) {\r
6475   case WM_INITDIALOG:\r
6476     move[0] = (char) lParam;\r
6477     move[1] = NULLCHAR;\r
6478     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6479     Translate(hDlg, DLG_TypeInName);\r
6480     hInput = GetDlgItem(hDlg, OPT_Name);\r
6481     SetWindowText(hInput, move);\r
6482     SetFocus(hInput);\r
6483     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6484     return FALSE;\r
6485 \r
6486   case WM_COMMAND:\r
6487     switch (LOWORD(wParam)) {\r
6488     case IDOK:\r
6489       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6490       appData.userName = strdup(move);\r
6491       SetUserLogo();\r
6492       SetGameInfo();\r
6493       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6494         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6495         DisplayTitle(move);\r
6496       }\r
6497 \r
6498 \r
6499       EndDialog(hDlg, TRUE);\r
6500       return TRUE;\r
6501     case IDCANCEL:\r
6502       EndDialog(hDlg, FALSE);\r
6503       return TRUE;\r
6504     default:\r
6505       break;\r
6506     }\r
6507     break;\r
6508   }\r
6509   return FALSE;\r
6510 }\r
6511 \r
6512 VOID\r
6513 PopUpNameDialog(char firstchar)\r
6514 {\r
6515     FARPROC lpProc;\r
6516     \r
6517       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6518       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6519         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6520       FreeProcInstance(lpProc);\r
6521 }\r
6522 \r
6523 /*---------------------------------------------------------------------------*\\r
6524  *\r
6525  *  Error dialogs\r
6526  * \r
6527 \*---------------------------------------------------------------------------*/\r
6528 \r
6529 /* Nonmodal error box */\r
6530 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6531                              WPARAM wParam, LPARAM lParam);\r
6532 \r
6533 VOID\r
6534 ErrorPopUp(char *title, char *content)\r
6535 {\r
6536   FARPROC lpProc;\r
6537   char *p, *q;\r
6538   BOOLEAN modal = hwndMain == NULL;\r
6539 \r
6540   p = content;\r
6541   q = errorMessage;\r
6542   while (*p) {\r
6543     if (*p == '\n') {\r
6544       if (modal) {\r
6545         *q++ = ' ';\r
6546         p++;\r
6547       } else {\r
6548         *q++ = '\r';\r
6549         *q++ = *p++;\r
6550       }\r
6551     } else {\r
6552       *q++ = *p++;\r
6553     }\r
6554   }\r
6555   *q = NULLCHAR;\r
6556   strncpy(errorTitle, title, sizeof(errorTitle));\r
6557   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6558   \r
6559   if (modal) {\r
6560     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6561   } else {\r
6562     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6563     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6564                  hwndMain, (DLGPROC)lpProc);\r
6565     FreeProcInstance(lpProc);\r
6566   }\r
6567 }\r
6568 \r
6569 VOID\r
6570 ErrorPopDown()\r
6571 {\r
6572   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6573   if (errorDialog == NULL) return;\r
6574   DestroyWindow(errorDialog);\r
6575   errorDialog = NULL;\r
6576   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6577 }\r
6578 \r
6579 LRESULT CALLBACK\r
6580 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6581 {\r
6582   HANDLE hwndText;\r
6583   RECT rChild;\r
6584 \r
6585   switch (message) {\r
6586   case WM_INITDIALOG:\r
6587     GetWindowRect(hDlg, &rChild);\r
6588 \r
6589     /*\r
6590     SetWindowPos(hDlg, NULL, rChild.left,\r
6591       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6592       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6593     */\r
6594 \r
6595     /* \r
6596         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6597         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6598         and it doesn't work when you resize the dialog.\r
6599         For now, just give it a default position.\r
6600     */\r
6601     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6602     Translate(hDlg, DLG_Error);\r
6603 \r
6604     errorDialog = hDlg;\r
6605     SetWindowText(hDlg, errorTitle);\r
6606     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6607     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6608     return FALSE;\r
6609 \r
6610   case WM_COMMAND:\r
6611     switch (LOWORD(wParam)) {\r
6612     case IDOK:\r
6613     case IDCANCEL:\r
6614       if (errorDialog == hDlg) errorDialog = NULL;\r
6615       DestroyWindow(hDlg);\r
6616       return TRUE;\r
6617 \r
6618     default:\r
6619       break;\r
6620     }\r
6621     break;\r
6622   }\r
6623   return FALSE;\r
6624 }\r
6625 \r
6626 #ifdef GOTHIC\r
6627 HWND gothicDialog = NULL;\r
6628 \r
6629 LRESULT CALLBACK\r
6630 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6631 {\r
6632   HANDLE hwndText;\r
6633   RECT rChild;\r
6634   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6635 \r
6636   switch (message) {\r
6637   case WM_INITDIALOG:\r
6638     GetWindowRect(hDlg, &rChild);\r
6639 \r
6640     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6641                                                              SWP_NOZORDER);\r
6642 \r
6643     /* \r
6644         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6645         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6646         and it doesn't work when you resize the dialog.\r
6647         For now, just give it a default position.\r
6648     */\r
6649     gothicDialog = hDlg;\r
6650     SetWindowText(hDlg, errorTitle);\r
6651     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6652     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6653     return FALSE;\r
6654 \r
6655   case WM_COMMAND:\r
6656     switch (LOWORD(wParam)) {\r
6657     case IDOK:\r
6658     case IDCANCEL:\r
6659       if (errorDialog == hDlg) errorDialog = NULL;\r
6660       DestroyWindow(hDlg);\r
6661       return TRUE;\r
6662 \r
6663     default:\r
6664       break;\r
6665     }\r
6666     break;\r
6667   }\r
6668   return FALSE;\r
6669 }\r
6670 \r
6671 VOID\r
6672 GothicPopUp(char *title, VariantClass variant)\r
6673 {\r
6674   FARPROC lpProc;\r
6675   static char *lastTitle;\r
6676 \r
6677   strncpy(errorTitle, title, sizeof(errorTitle));\r
6678   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6679 \r
6680   if(lastTitle != title && gothicDialog != NULL) {\r
6681     DestroyWindow(gothicDialog);\r
6682     gothicDialog = NULL;\r
6683   }\r
6684   if(variant != VariantNormal && gothicDialog == NULL) {\r
6685     title = lastTitle;\r
6686     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6687     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6688                  hwndMain, (DLGPROC)lpProc);\r
6689     FreeProcInstance(lpProc);\r
6690   }\r
6691 }\r
6692 #endif\r
6693 \r
6694 /*---------------------------------------------------------------------------*\\r
6695  *\r
6696  *  Ics Interaction console functions\r
6697  *\r
6698 \*---------------------------------------------------------------------------*/\r
6699 \r
6700 #define HISTORY_SIZE 64\r
6701 static char *history[HISTORY_SIZE];\r
6702 int histIn = 0, histP = 0;\r
6703 \r
6704 VOID\r
6705 SaveInHistory(char *cmd)\r
6706 {\r
6707   if (history[histIn] != NULL) {\r
6708     free(history[histIn]);\r
6709     history[histIn] = NULL;\r
6710   }\r
6711   if (*cmd == NULLCHAR) return;\r
6712   history[histIn] = StrSave(cmd);\r
6713   histIn = (histIn + 1) % HISTORY_SIZE;\r
6714   if (history[histIn] != NULL) {\r
6715     free(history[histIn]);\r
6716     history[histIn] = NULL;\r
6717   }\r
6718   histP = histIn;\r
6719 }\r
6720 \r
6721 char *\r
6722 PrevInHistory(char *cmd)\r
6723 {\r
6724   int newhp;\r
6725   if (histP == histIn) {\r
6726     if (history[histIn] != NULL) free(history[histIn]);\r
6727     history[histIn] = StrSave(cmd);\r
6728   }\r
6729   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6730   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6731   histP = newhp;\r
6732   return history[histP];\r
6733 }\r
6734 \r
6735 char *\r
6736 NextInHistory()\r
6737 {\r
6738   if (histP == histIn) return NULL;\r
6739   histP = (histP + 1) % HISTORY_SIZE;\r
6740   return history[histP];   \r
6741 }\r
6742 \r
6743 HMENU\r
6744 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6745 {\r
6746   HMENU hmenu, h;\r
6747   int i = 0;\r
6748   hmenu = LoadMenu(hInst, "TextMenu");\r
6749   h = GetSubMenu(hmenu, 0);\r
6750   while (e->item) {\r
6751     if (strcmp(e->item, "-") == 0) {\r
6752       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6753     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6754       int flags = MF_STRING, j = 0;\r
6755       if (e->item[0] == '|') {\r
6756         flags |= MF_MENUBARBREAK;\r
6757         j++;\r
6758       }\r
6759       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6760       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6761     }\r
6762     e++;\r
6763     i++;\r
6764   } \r
6765   return hmenu;\r
6766 }\r
6767 \r
6768 WNDPROC consoleTextWindowProc;\r
6769 \r
6770 void\r
6771 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6772 {\r
6773   char buf[MSG_SIZ], name[MSG_SIZ];\r
6774   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6775   CHARRANGE sel;\r
6776 \r
6777   if (!getname) {\r
6778     SetWindowText(hInput, command);\r
6779     if (immediate) {\r
6780       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6781     } else {\r
6782       sel.cpMin = 999999;\r
6783       sel.cpMax = 999999;\r
6784       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6785       SetFocus(hInput);\r
6786     }\r
6787     return;\r
6788   }    \r
6789   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6790   if (sel.cpMin == sel.cpMax) {\r
6791     /* Expand to surrounding word */\r
6792     TEXTRANGE tr;\r
6793     do {\r
6794       tr.chrg.cpMax = sel.cpMin;\r
6795       tr.chrg.cpMin = --sel.cpMin;\r
6796       if (sel.cpMin < 0) break;\r
6797       tr.lpstrText = name;\r
6798       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6799     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6800     sel.cpMin++;\r
6801 \r
6802     do {\r
6803       tr.chrg.cpMin = sel.cpMax;\r
6804       tr.chrg.cpMax = ++sel.cpMax;\r
6805       tr.lpstrText = name;\r
6806       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6807     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6808     sel.cpMax--;\r
6809 \r
6810     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6811       MessageBeep(MB_ICONEXCLAMATION);\r
6812       return;\r
6813     }\r
6814     tr.chrg = sel;\r
6815     tr.lpstrText = name;\r
6816     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6817   } else {\r
6818     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6819       MessageBeep(MB_ICONEXCLAMATION);\r
6820       return;\r
6821     }\r
6822     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6823   }\r
6824   if (immediate) {\r
6825     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6826     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6827     SetWindowText(hInput, buf);\r
6828     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6829   } else {\r
6830     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6831       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6832     SetWindowText(hInput, buf);\r
6833     sel.cpMin = 999999;\r
6834     sel.cpMax = 999999;\r
6835     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6836     SetFocus(hInput);\r
6837   }\r
6838 }\r
6839 \r
6840 LRESULT CALLBACK \r
6841 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6842 {\r
6843   HWND hInput;\r
6844   CHARRANGE sel;\r
6845 \r
6846   switch (message) {\r
6847   case WM_KEYDOWN:\r
6848     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6849     if(wParam=='R') return 0;\r
6850     switch (wParam) {\r
6851     case VK_PRIOR:\r
6852       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6853       return 0;\r
6854     case VK_NEXT:\r
6855       sel.cpMin = 999999;\r
6856       sel.cpMax = 999999;\r
6857       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6858       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6859       return 0;\r
6860     }\r
6861     break;\r
6862   case WM_CHAR:\r
6863    if(wParam != '\022') {\r
6864     if (wParam == '\t') {\r
6865       if (GetKeyState(VK_SHIFT) < 0) {\r
6866         /* shifted */\r
6867         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6868         if (buttonDesc[0].hwnd) {\r
6869           SetFocus(buttonDesc[0].hwnd);\r
6870         } else {\r
6871           SetFocus(hwndMain);\r
6872         }\r
6873       } else {\r
6874         /* unshifted */\r
6875         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6876       }\r
6877     } else {\r
6878       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6879       JAWS_DELETE( SetFocus(hInput); )\r
6880       SendMessage(hInput, message, wParam, lParam);\r
6881     }\r
6882     return 0;\r
6883    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6884    lParam = -1;\r
6885   case WM_RBUTTONDOWN:\r
6886     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6887       /* Move selection here if it was empty */\r
6888       POINT pt;\r
6889       pt.x = LOWORD(lParam);\r
6890       pt.y = HIWORD(lParam);\r
6891       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6892       if (sel.cpMin == sel.cpMax) {\r
6893         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6894         sel.cpMax = sel.cpMin;\r
6895         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6896       }\r
6897       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6898 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6899       POINT pt;\r
6900       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6901       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6902       if (sel.cpMin == sel.cpMax) {\r
6903         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6904         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6905       }\r
6906       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6907         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6908       }\r
6909       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6910       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6911       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6912       MenuPopup(hwnd, pt, hmenu, -1);\r
6913 }\r
6914     }\r
6915     return 0;\r
6916   case WM_RBUTTONUP:\r
6917     if (GetKeyState(VK_SHIFT) & ~1) {\r
6918       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6919         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6920     }\r
6921     return 0;\r
6922   case WM_PASTE:\r
6923     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6924     SetFocus(hInput);\r
6925     return SendMessage(hInput, message, wParam, lParam);\r
6926   case WM_MBUTTONDOWN:\r
6927     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6928   case WM_COMMAND:\r
6929     switch (LOWORD(wParam)) {\r
6930     case IDM_QuickPaste:\r
6931       {\r
6932         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6933         if (sel.cpMin == sel.cpMax) {\r
6934           MessageBeep(MB_ICONEXCLAMATION);\r
6935           return 0;\r
6936         }\r
6937         SendMessage(hwnd, WM_COPY, 0, 0);\r
6938         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6939         SendMessage(hInput, WM_PASTE, 0, 0);\r
6940         SetFocus(hInput);\r
6941         return 0;\r
6942       }\r
6943     case IDM_Cut:\r
6944       SendMessage(hwnd, WM_CUT, 0, 0);\r
6945       return 0;\r
6946     case IDM_Paste:\r
6947       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6948       return 0;\r
6949     case IDM_Copy:\r
6950       SendMessage(hwnd, WM_COPY, 0, 0);\r
6951       return 0;\r
6952     default:\r
6953       {\r
6954         int i = LOWORD(wParam) - IDM_CommandX;\r
6955         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6956             icsTextMenuEntry[i].command != NULL) {\r
6957           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6958                    icsTextMenuEntry[i].getname,\r
6959                    icsTextMenuEntry[i].immediate);\r
6960           return 0;\r
6961         }\r
6962       }\r
6963       break;\r
6964     }\r
6965     break;\r
6966   }\r
6967   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6968 }\r
6969 \r
6970 WNDPROC consoleInputWindowProc;\r
6971 \r
6972 LRESULT CALLBACK\r
6973 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6974 {\r
6975   char buf[MSG_SIZ];\r
6976   char *p;\r
6977   static BOOL sendNextChar = FALSE;\r
6978   static BOOL quoteNextChar = FALSE;\r
6979   InputSource *is = consoleInputSource;\r
6980   CHARFORMAT cf;\r
6981   CHARRANGE sel;\r
6982 \r
6983   switch (message) {\r
6984   case WM_CHAR:\r
6985     if (!appData.localLineEditing || sendNextChar) {\r
6986       is->buf[0] = (CHAR) wParam;\r
6987       is->count = 1;\r
6988       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6989       sendNextChar = FALSE;\r
6990       return 0;\r
6991     }\r
6992     if (quoteNextChar) {\r
6993       buf[0] = (char) wParam;\r
6994       buf[1] = NULLCHAR;\r
6995       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6996       quoteNextChar = FALSE;\r
6997       return 0;\r
6998     }\r
6999     switch (wParam) {\r
7000     case '\r':   /* Enter key */\r
7001       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7002       if (consoleEcho) SaveInHistory(is->buf);\r
7003       is->buf[is->count++] = '\n';\r
7004       is->buf[is->count] = NULLCHAR;\r
7005       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7006       if (consoleEcho) {\r
7007         ConsoleOutput(is->buf, is->count, TRUE);\r
7008       } else if (appData.localLineEditing) {\r
7009         ConsoleOutput("\n", 1, TRUE);\r
7010       }\r
7011       /* fall thru */\r
7012     case '\033': /* Escape key */\r
7013       SetWindowText(hwnd, "");\r
7014       cf.cbSize = sizeof(CHARFORMAT);\r
7015       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7016       if (consoleEcho) {\r
7017         cf.crTextColor = textAttribs[ColorNormal].color;\r
7018       } else {\r
7019         cf.crTextColor = COLOR_ECHOOFF;\r
7020       }\r
7021       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7022       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7023       return 0;\r
7024     case '\t':   /* Tab key */\r
7025       if (GetKeyState(VK_SHIFT) < 0) {\r
7026         /* shifted */\r
7027         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7028       } else {\r
7029         /* unshifted */\r
7030         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7031         if (buttonDesc[0].hwnd) {\r
7032           SetFocus(buttonDesc[0].hwnd);\r
7033         } else {\r
7034           SetFocus(hwndMain);\r
7035         }\r
7036       }\r
7037       return 0;\r
7038     case '\023': /* Ctrl+S */\r
7039       sendNextChar = TRUE;\r
7040       return 0;\r
7041     case '\021': /* Ctrl+Q */\r
7042       quoteNextChar = TRUE;\r
7043       return 0;\r
7044     JAWS_REPLAY\r
7045     default:\r
7046       break;\r
7047     }\r
7048     break;\r
7049   case WM_KEYDOWN:\r
7050     switch (wParam) {\r
7051     case VK_UP:\r
7052       GetWindowText(hwnd, buf, MSG_SIZ);\r
7053       p = PrevInHistory(buf);\r
7054       if (p != NULL) {\r
7055         SetWindowText(hwnd, p);\r
7056         sel.cpMin = 999999;\r
7057         sel.cpMax = 999999;\r
7058         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7059         return 0;\r
7060       }\r
7061       break;\r
7062     case VK_DOWN:\r
7063       p = NextInHistory();\r
7064       if (p != NULL) {\r
7065         SetWindowText(hwnd, p);\r
7066         sel.cpMin = 999999;\r
7067         sel.cpMax = 999999;\r
7068         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7069         return 0;\r
7070       }\r
7071       break;\r
7072     case VK_HOME:\r
7073     case VK_END:\r
7074       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7075       /* fall thru */\r
7076     case VK_PRIOR:\r
7077     case VK_NEXT:\r
7078       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7079       return 0;\r
7080     }\r
7081     break;\r
7082   case WM_MBUTTONDOWN:\r
7083     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7084       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7085     break;\r
7086   case WM_RBUTTONUP:\r
7087     if (GetKeyState(VK_SHIFT) & ~1) {\r
7088       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7089         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7090     } else {\r
7091       POINT pt;\r
7092       HMENU hmenu;\r
7093       hmenu = LoadMenu(hInst, "InputMenu");\r
7094       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7095       if (sel.cpMin == sel.cpMax) {\r
7096         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7097         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7098       }\r
7099       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7100         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7101       }\r
7102       pt.x = LOWORD(lParam);\r
7103       pt.y = HIWORD(lParam);\r
7104       MenuPopup(hwnd, pt, hmenu, -1);\r
7105     }\r
7106     return 0;\r
7107   case WM_COMMAND:\r
7108     switch (LOWORD(wParam)) { \r
7109     case IDM_Undo:\r
7110       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7111       return 0;\r
7112     case IDM_SelectAll:\r
7113       sel.cpMin = 0;\r
7114       sel.cpMax = -1; /*999999?*/\r
7115       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7116       return 0;\r
7117     case IDM_Cut:\r
7118       SendMessage(hwnd, WM_CUT, 0, 0);\r
7119       return 0;\r
7120     case IDM_Paste:\r
7121       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7122       return 0;\r
7123     case IDM_Copy:\r
7124       SendMessage(hwnd, WM_COPY, 0, 0);\r
7125       return 0;\r
7126     }\r
7127     break;\r
7128   }\r
7129   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7130 }\r
7131 \r
7132 #define CO_MAX  100000\r
7133 #define CO_TRIM   1000\r
7134 \r
7135 LRESULT CALLBACK\r
7136 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7137 {\r
7138   static SnapData sd;\r
7139   HWND hText, hInput;\r
7140   RECT rect;\r
7141   static int sizeX, sizeY;\r
7142   int newSizeX, newSizeY;\r
7143   MINMAXINFO *mmi;\r
7144   WORD wMask;\r
7145 \r
7146   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7147   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7148 \r
7149   switch (message) {\r
7150   case WM_NOTIFY:\r
7151     if (((NMHDR*)lParam)->code == EN_LINK)\r
7152     {\r
7153       ENLINK *pLink = (ENLINK*)lParam;\r
7154       if (pLink->msg == WM_LBUTTONUP)\r
7155       {\r
7156         TEXTRANGE tr;\r
7157 \r
7158         tr.chrg = pLink->chrg;\r
7159         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7160         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7161         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7162         free(tr.lpstrText);\r
7163       }\r
7164     }\r
7165     break;\r
7166   case WM_INITDIALOG: /* message: initialize dialog box */\r
7167     hwndConsole = hDlg;\r
7168     SetFocus(hInput);\r
7169     consoleTextWindowProc = (WNDPROC)\r
7170       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7171     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7172     consoleInputWindowProc = (WNDPROC)\r
7173       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7174     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7175     Colorize(ColorNormal, TRUE);\r
7176     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7177     ChangedConsoleFont();\r
7178     GetClientRect(hDlg, &rect);\r
7179     sizeX = rect.right;\r
7180     sizeY = rect.bottom;\r
7181     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7182         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7183       WINDOWPLACEMENT wp;\r
7184       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7185       wp.length = sizeof(WINDOWPLACEMENT);\r
7186       wp.flags = 0;\r
7187       wp.showCmd = SW_SHOW;\r
7188       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7189       wp.rcNormalPosition.left = wpConsole.x;\r
7190       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7191       wp.rcNormalPosition.top = wpConsole.y;\r
7192       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7193       SetWindowPlacement(hDlg, &wp);\r
7194     }\r
7195 \r
7196    // [HGM] Chessknight's change 2004-07-13\r
7197    else { /* Determine Defaults */\r
7198        WINDOWPLACEMENT wp;\r
7199        wpConsole.x = wpMain.width + 1;\r
7200        wpConsole.y = wpMain.y;\r
7201        wpConsole.width = screenWidth -  wpMain.width;\r
7202        wpConsole.height = wpMain.height;\r
7203        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7204        wp.length = sizeof(WINDOWPLACEMENT);\r
7205        wp.flags = 0;\r
7206        wp.showCmd = SW_SHOW;\r
7207        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7208        wp.rcNormalPosition.left = wpConsole.x;\r
7209        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7210        wp.rcNormalPosition.top = wpConsole.y;\r
7211        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7212        SetWindowPlacement(hDlg, &wp);\r
7213     }\r
7214 \r
7215    // Allow hText to highlight URLs and send notifications on them\r
7216    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7217    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7218    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7219    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7220 \r
7221     return FALSE;\r
7222 \r
7223   case WM_SETFOCUS:\r
7224     SetFocus(hInput);\r
7225     return 0;\r
7226 \r
7227   case WM_CLOSE:\r
7228     ExitEvent(0);\r
7229     /* not reached */\r
7230     break;\r
7231 \r
7232   case WM_SIZE:\r
7233     if (IsIconic(hDlg)) break;\r
7234     newSizeX = LOWORD(lParam);\r
7235     newSizeY = HIWORD(lParam);\r
7236     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7237       RECT rectText, rectInput;\r
7238       POINT pt;\r
7239       int newTextHeight, newTextWidth;\r
7240       GetWindowRect(hText, &rectText);\r
7241       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7242       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7243       if (newTextHeight < 0) {\r
7244         newSizeY += -newTextHeight;\r
7245         newTextHeight = 0;\r
7246       }\r
7247       SetWindowPos(hText, NULL, 0, 0,\r
7248         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7249       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7250       pt.x = rectInput.left;\r
7251       pt.y = rectInput.top + newSizeY - sizeY;\r
7252       ScreenToClient(hDlg, &pt);\r
7253       SetWindowPos(hInput, NULL, \r
7254         pt.x, pt.y, /* needs client coords */   \r
7255         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7256         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7257     }\r
7258     sizeX = newSizeX;\r
7259     sizeY = newSizeY;\r
7260     break;\r
7261 \r
7262   case WM_GETMINMAXINFO:\r
7263     /* Prevent resizing window too small */\r
7264     mmi = (MINMAXINFO *) lParam;\r
7265     mmi->ptMinTrackSize.x = 100;\r
7266     mmi->ptMinTrackSize.y = 100;\r
7267     break;\r
7268 \r
7269   /* [AS] Snapping */\r
7270   case WM_ENTERSIZEMOVE:\r
7271     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7272 \r
7273   case WM_SIZING:\r
7274     return OnSizing( &sd, hDlg, wParam, lParam );\r
7275 \r
7276   case WM_MOVING:\r
7277     return OnMoving( &sd, hDlg, wParam, lParam );\r
7278 \r
7279   case WM_EXITSIZEMOVE:\r
7280         UpdateICSWidth(hText);\r
7281     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7282   }\r
7283 \r
7284   return DefWindowProc(hDlg, message, wParam, lParam);\r
7285 }\r
7286 \r
7287 \r
7288 VOID\r
7289 ConsoleCreate()\r
7290 {\r
7291   HWND hCons;\r
7292   if (hwndConsole) return;\r
7293   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7294   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7295 }\r
7296 \r
7297 \r
7298 VOID\r
7299 ConsoleOutput(char* data, int length, int forceVisible)\r
7300 {\r
7301   HWND hText;\r
7302   int trim, exlen;\r
7303   char *p, *q;\r
7304   char buf[CO_MAX+1];\r
7305   POINT pEnd;\r
7306   RECT rect;\r
7307   static int delayLF = 0;\r
7308   CHARRANGE savesel, sel;\r
7309 \r
7310   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7311   p = data;\r
7312   q = buf;\r
7313   if (delayLF) {\r
7314     *q++ = '\r';\r
7315     *q++ = '\n';\r
7316     delayLF = 0;\r
7317   }\r
7318   while (length--) {\r
7319     if (*p == '\n') {\r
7320       if (*++p) {\r
7321         *q++ = '\r';\r
7322         *q++ = '\n';\r
7323       } else {\r
7324         delayLF = 1;\r
7325       }\r
7326     } else if (*p == '\007') {\r
7327        MyPlaySound(&sounds[(int)SoundBell]);\r
7328        p++;\r
7329     } else {\r
7330       *q++ = *p++;\r
7331     }\r
7332   }\r
7333   *q = NULLCHAR;\r
7334   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7335   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7336   /* Save current selection */\r
7337   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7338   exlen = GetWindowTextLength(hText);\r
7339   /* Find out whether current end of text is visible */\r
7340   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7341   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7342   /* Trim existing text if it's too long */\r
7343   if (exlen + (q - buf) > CO_MAX) {\r
7344     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7345     sel.cpMin = 0;\r
7346     sel.cpMax = trim;\r
7347     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7348     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7349     exlen -= trim;\r
7350     savesel.cpMin -= trim;\r
7351     savesel.cpMax -= trim;\r
7352     if (exlen < 0) exlen = 0;\r
7353     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7354     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7355   }\r
7356   /* Append the new text */\r
7357   sel.cpMin = exlen;\r
7358   sel.cpMax = exlen;\r
7359   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7360   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7361   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7362   if (forceVisible || exlen == 0 ||\r
7363       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7364        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7365     /* Scroll to make new end of text visible if old end of text\r
7366        was visible or new text is an echo of user typein */\r
7367     sel.cpMin = 9999999;\r
7368     sel.cpMax = 9999999;\r
7369     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7370     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7371     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7372     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7373   }\r
7374   if (savesel.cpMax == exlen || forceVisible) {\r
7375     /* Move insert point to new end of text if it was at the old\r
7376        end of text or if the new text is an echo of user typein */\r
7377     sel.cpMin = 9999999;\r
7378     sel.cpMax = 9999999;\r
7379     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7380   } else {\r
7381     /* Restore previous selection */\r
7382     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7383   }\r
7384   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7385 }\r
7386 \r
7387 /*---------*/\r
7388 \r
7389 \r
7390 void\r
7391 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7392 {\r
7393   char buf[100];\r
7394   char *str;\r
7395   COLORREF oldFg, oldBg;\r
7396   HFONT oldFont;\r
7397   RECT rect;\r
7398 \r
7399   if(copyNumber > 1)
7400     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7401 \r
7402   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7403   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7404   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7405 \r
7406   rect.left = x;\r
7407   rect.right = x + squareSize;\r
7408   rect.top  = y;\r
7409   rect.bottom = y + squareSize;\r
7410   str = buf;\r
7411 \r
7412   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7413                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7414              y, ETO_CLIPPED|ETO_OPAQUE,\r
7415              &rect, str, strlen(str), NULL);\r
7416 \r
7417   (void) SetTextColor(hdc, oldFg);\r
7418   (void) SetBkColor(hdc, oldBg);\r
7419   (void) SelectObject(hdc, oldFont);\r
7420 }\r
7421 \r
7422 void\r
7423 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7424               RECT *rect, char *color, char *flagFell)\r
7425 {\r
7426   char buf[100];\r
7427   char *str;\r
7428   COLORREF oldFg, oldBg;\r
7429   HFONT oldFont;\r
7430 \r
7431   if (appData.clockMode) {\r
7432     if (tinyLayout)\r
7433       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7434     else\r
7435       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7436     str = buf;\r
7437   } else {\r
7438     str = color;\r
7439   }\r
7440 \r
7441   if (highlight) {\r
7442     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7443     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7444   } else {\r
7445     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7446     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7447   }\r
7448   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7449 \r
7450   JAWS_SILENCE\r
7451 \r
7452   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7453              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7454              rect, str, strlen(str), NULL);\r
7455   if(logoHeight > 0 && appData.clockMode) {\r
7456       RECT r;\r
7457       str += strlen(color)+2;\r
7458       r.top = rect->top + logoHeight/2;\r
7459       r.left = rect->left;\r
7460       r.right = rect->right;\r
7461       r.bottom = rect->bottom;\r
7462       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7463                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7464                  &r, str, strlen(str), NULL);\r
7465   }\r
7466   (void) SetTextColor(hdc, oldFg);\r
7467   (void) SetBkColor(hdc, oldBg);\r
7468   (void) SelectObject(hdc, oldFont);\r
7469 }\r
7470 \r
7471 \r
7472 int\r
7473 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7474            OVERLAPPED *ovl)\r
7475 {\r
7476   int ok, err;\r
7477 \r
7478   /* [AS]  */\r
7479   if( count <= 0 ) {\r
7480     if (appData.debugMode) {\r
7481       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7482     }\r
7483 \r
7484     return ERROR_INVALID_USER_BUFFER;\r
7485   }\r
7486 \r
7487   ResetEvent(ovl->hEvent);\r
7488   ovl->Offset = ovl->OffsetHigh = 0;\r
7489   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7490   if (ok) {\r
7491     err = NO_ERROR;\r
7492   } else {\r
7493     err = GetLastError();\r
7494     if (err == ERROR_IO_PENDING) {\r
7495       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7496       if (ok)\r
7497         err = NO_ERROR;\r
7498       else\r
7499         err = GetLastError();\r
7500     }\r
7501   }\r
7502   return err;\r
7503 }\r
7504 \r
7505 int\r
7506 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7507             OVERLAPPED *ovl)\r
7508 {\r
7509   int ok, err;\r
7510 \r
7511   ResetEvent(ovl->hEvent);\r
7512   ovl->Offset = ovl->OffsetHigh = 0;\r
7513   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7514   if (ok) {\r
7515     err = NO_ERROR;\r
7516   } else {\r
7517     err = GetLastError();\r
7518     if (err == ERROR_IO_PENDING) {\r
7519       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7520       if (ok)\r
7521         err = NO_ERROR;\r
7522       else\r
7523         err = GetLastError();\r
7524     }\r
7525   }\r
7526   return err;\r
7527 }\r
7528 \r
7529 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7530 void CheckForInputBufferFull( InputSource * is )\r
7531 {\r
7532     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7533         /* Look for end of line */\r
7534         char * p = is->buf;\r
7535         \r
7536         while( p < is->next && *p != '\n' ) {\r
7537             p++;\r
7538         }\r
7539 \r
7540         if( p >= is->next ) {\r
7541             if (appData.debugMode) {\r
7542                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7543             }\r
7544 \r
7545             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7546             is->count = (DWORD) -1;\r
7547             is->next = is->buf;\r
7548         }\r
7549     }\r
7550 }\r
7551 \r
7552 DWORD\r
7553 InputThread(LPVOID arg)\r
7554 {\r
7555   InputSource *is;\r
7556   OVERLAPPED ovl;\r
7557 \r
7558   is = (InputSource *) arg;\r
7559   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7560   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7561   while (is->hThread != NULL) {\r
7562     is->error = DoReadFile(is->hFile, is->next,\r
7563                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7564                            &is->count, &ovl);\r
7565     if (is->error == NO_ERROR) {\r
7566       is->next += is->count;\r
7567     } else {\r
7568       if (is->error == ERROR_BROKEN_PIPE) {\r
7569         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7570         is->count = 0;\r
7571       } else {\r
7572         is->count = (DWORD) -1;\r
7573         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7574         break; \r
7575       }\r
7576     }\r
7577 \r
7578     CheckForInputBufferFull( is );\r
7579 \r
7580     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7581 \r
7582     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7583 \r
7584     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7585   }\r
7586 \r
7587   CloseHandle(ovl.hEvent);\r
7588   CloseHandle(is->hFile);\r
7589 \r
7590   if (appData.debugMode) {\r
7591     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7592   }\r
7593 \r
7594   return 0;\r
7595 }\r
7596 \r
7597 \r
7598 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7599 DWORD\r
7600 NonOvlInputThread(LPVOID arg)\r
7601 {\r
7602   InputSource *is;\r
7603   char *p, *q;\r
7604   int i;\r
7605   char prev;\r
7606 \r
7607   is = (InputSource *) arg;\r
7608   while (is->hThread != NULL) {\r
7609     is->error = ReadFile(is->hFile, is->next,\r
7610                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7611                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7612     if (is->error == NO_ERROR) {\r
7613       /* Change CRLF to LF */\r
7614       if (is->next > is->buf) {\r
7615         p = is->next - 1;\r
7616         i = is->count + 1;\r
7617       } else {\r
7618         p = is->next;\r
7619         i = is->count;\r
7620       }\r
7621       q = p;\r
7622       prev = NULLCHAR;\r
7623       while (i > 0) {\r
7624         if (prev == '\r' && *p == '\n') {\r
7625           *(q-1) = '\n';\r
7626           is->count--;\r
7627         } else { \r
7628           *q++ = *p;\r
7629         }\r
7630         prev = *p++;\r
7631         i--;\r
7632       }\r
7633       *q = NULLCHAR;\r
7634       is->next = q;\r
7635     } else {\r
7636       if (is->error == ERROR_BROKEN_PIPE) {\r
7637         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7638         is->count = 0; \r
7639       } else {\r
7640         is->count = (DWORD) -1;\r
7641       }\r
7642     }\r
7643 \r
7644     CheckForInputBufferFull( is );\r
7645 \r
7646     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7647 \r
7648     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7649 \r
7650     if (is->count < 0) break;  /* Quit on error */\r
7651   }\r
7652   CloseHandle(is->hFile);\r
7653   return 0;\r
7654 }\r
7655 \r
7656 DWORD\r
7657 SocketInputThread(LPVOID arg)\r
7658 {\r
7659   InputSource *is;\r
7660 \r
7661   is = (InputSource *) arg;\r
7662   while (is->hThread != NULL) {\r
7663     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7664     if ((int)is->count == SOCKET_ERROR) {\r
7665       is->count = (DWORD) -1;\r
7666       is->error = WSAGetLastError();\r
7667     } else {\r
7668       is->error = NO_ERROR;\r
7669       is->next += is->count;\r
7670       if (is->count == 0 && is->second == is) {\r
7671         /* End of file on stderr; quit with no message */\r
7672         break;\r
7673       }\r
7674     }\r
7675     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7676 \r
7677     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7678 \r
7679     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7680   }\r
7681   return 0;\r
7682 }\r
7683 \r
7684 VOID\r
7685 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7686 {\r
7687   InputSource *is;\r
7688 \r
7689   is = (InputSource *) lParam;\r
7690   if (is->lineByLine) {\r
7691     /* Feed in lines one by one */\r
7692     char *p = is->buf;\r
7693     char *q = p;\r
7694     while (q < is->next) {\r
7695       if (*q++ == '\n') {\r
7696         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7697         p = q;\r
7698       }\r
7699     }\r
7700     \r
7701     /* Move any partial line to the start of the buffer */\r
7702     q = is->buf;\r
7703     while (p < is->next) {\r
7704       *q++ = *p++;\r
7705     }\r
7706     is->next = q;\r
7707 \r
7708     if (is->error != NO_ERROR || is->count == 0) {\r
7709       /* Notify backend of the error.  Note: If there was a partial\r
7710          line at the end, it is not flushed through. */\r
7711       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7712     }\r
7713   } else {\r
7714     /* Feed in the whole chunk of input at once */\r
7715     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7716     is->next = is->buf;\r
7717   }\r
7718 }\r
7719 \r
7720 /*---------------------------------------------------------------------------*\\r
7721  *\r
7722  *  Menu enables. Used when setting various modes.\r
7723  *\r
7724 \*---------------------------------------------------------------------------*/\r
7725 \r
7726 typedef struct {\r
7727   int item;\r
7728   int flags;\r
7729 } Enables;\r
7730 \r
7731 VOID\r
7732 GreyRevert(Boolean grey)\r
7733 { // [HGM] vari: for retracting variations in local mode\r
7734   HMENU hmenu = GetMenu(hwndMain);\r
7735   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7736   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7737 }\r
7738 \r
7739 VOID\r
7740 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7741 {\r
7742   while (enab->item > 0) {\r
7743     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7744     enab++;\r
7745   }\r
7746 }\r
7747 \r
7748 Enables gnuEnables[] = {\r
7749   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7751   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7753   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7754   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7755   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7756   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7757   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7762 \r
7763   // Needed to switch from ncp to GNU mode on Engine Load\r
7764   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7765   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7766   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7767   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7768   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7769   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7770   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7771   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7772   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7773   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7774   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7775   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7776   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7777   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7778   { -1, -1 }\r
7779 };\r
7780 \r
7781 Enables icsEnables[] = {\r
7782   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7790   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7797   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7801   { -1, -1 }\r
7802 };\r
7803 \r
7804 #if ZIPPY\r
7805 Enables zippyEnables[] = {\r
7806   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7807   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7808   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7809   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7810   { -1, -1 }\r
7811 };\r
7812 #endif\r
7813 \r
7814 Enables ncpEnables[] = {\r
7815   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7824   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7825   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7837   { -1, -1 }\r
7838 };\r
7839 \r
7840 Enables trainingOnEnables[] = {\r
7841   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7850   { -1, -1 }\r
7851 };\r
7852 \r
7853 Enables trainingOffEnables[] = {\r
7854   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7863   { -1, -1 }\r
7864 };\r
7865 \r
7866 /* These modify either ncpEnables or gnuEnables */\r
7867 Enables cmailEnables[] = {\r
7868   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7870   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7871   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7874   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7875   { -1, -1 }\r
7876 };\r
7877 \r
7878 Enables machineThinkingEnables[] = {\r
7879   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7892 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7895   { -1, -1 }\r
7896 };\r
7897 \r
7898 Enables userThinkingEnables[] = {\r
7899   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7900   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7901   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7912 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7915   { -1, -1 }\r
7916 };\r
7917 \r
7918 /*---------------------------------------------------------------------------*\\r
7919  *\r
7920  *  Front-end interface functions exported by XBoard.\r
7921  *  Functions appear in same order as prototypes in frontend.h.\r
7922  * \r
7923 \*---------------------------------------------------------------------------*/\r
7924 VOID\r
7925 CheckMark(UINT item, int state)\r
7926 {\r
7927     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7928 }\r
7929 \r
7930 VOID\r
7931 ModeHighlight()\r
7932 {\r
7933   static UINT prevChecked = 0;\r
7934   static int prevPausing = 0;\r
7935   UINT nowChecked;\r
7936 \r
7937   if (pausing != prevPausing) {\r
7938     prevPausing = pausing;\r
7939     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7940                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7941     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7942   }\r
7943 \r
7944   switch (gameMode) {\r
7945   case BeginningOfGame:\r
7946     if (appData.icsActive)\r
7947       nowChecked = IDM_IcsClient;\r
7948     else if (appData.noChessProgram)\r
7949       nowChecked = IDM_EditGame;\r
7950     else\r
7951       nowChecked = IDM_MachineBlack;\r
7952     break;\r
7953   case MachinePlaysBlack:\r
7954     nowChecked = IDM_MachineBlack;\r
7955     break;\r
7956   case MachinePlaysWhite:\r
7957     nowChecked = IDM_MachineWhite;\r
7958     break;\r
7959   case TwoMachinesPlay:\r
7960     nowChecked = IDM_TwoMachines;\r
7961     break;\r
7962   case AnalyzeMode:\r
7963     nowChecked = IDM_AnalysisMode;\r
7964     break;\r
7965   case AnalyzeFile:\r
7966     nowChecked = IDM_AnalyzeFile;\r
7967     break;\r
7968   case EditGame:\r
7969     nowChecked = IDM_EditGame;\r
7970     break;\r
7971   case PlayFromGameFile:\r
7972     nowChecked = IDM_LoadGame;\r
7973     break;\r
7974   case EditPosition:\r
7975     nowChecked = IDM_EditPosition;\r
7976     break;\r
7977   case Training:\r
7978     nowChecked = IDM_Training;\r
7979     break;\r
7980   case IcsPlayingWhite:\r
7981   case IcsPlayingBlack:\r
7982   case IcsObserving:\r
7983   case IcsIdle:\r
7984     nowChecked = IDM_IcsClient;\r
7985     break;\r
7986   default:\r
7987   case EndOfGame:\r
7988     nowChecked = 0;\r
7989     break;\r
7990   }\r
7991   CheckMark(prevChecked, MF_UNCHECKED);\r
7992   CheckMark(nowChecked, MF_CHECKED);\r
7993   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
7994 \r
7995   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7996     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7997                           MF_BYCOMMAND|MF_ENABLED);\r
7998   } else {\r
7999     (void) EnableMenuItem(GetMenu(hwndMain), \r
8000                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8001   }\r
8002 \r
8003   prevChecked = nowChecked;\r
8004 \r
8005   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8006   if (appData.icsActive) {\r
8007        if (appData.icsEngineAnalyze) {\r
8008                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8009        } else {\r
8010                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8011        }\r
8012   }\r
8013   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8014 }\r
8015 \r
8016 VOID\r
8017 SetICSMode()\r
8018 {\r
8019   HMENU hmenu = GetMenu(hwndMain);\r
8020   SetMenuEnables(hmenu, icsEnables);\r
8021   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8022     MF_BYCOMMAND|MF_ENABLED);\r
8023 #if ZIPPY\r
8024   if (appData.zippyPlay) {\r
8025     SetMenuEnables(hmenu, zippyEnables);\r
8026     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8027          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8028           MF_BYCOMMAND|MF_ENABLED);\r
8029   }\r
8030 #endif\r
8031 }\r
8032 \r
8033 VOID\r
8034 SetGNUMode()\r
8035 {\r
8036   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8037 }\r
8038 \r
8039 VOID\r
8040 SetNCPMode()\r
8041 {\r
8042   HMENU hmenu = GetMenu(hwndMain);\r
8043   SetMenuEnables(hmenu, ncpEnables);\r
8044     DrawMenuBar(hwndMain);\r
8045 }\r
8046 \r
8047 VOID\r
8048 SetCmailMode()\r
8049 {\r
8050   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8051 }\r
8052 \r
8053 VOID \r
8054 SetTrainingModeOn()\r
8055 {\r
8056   int i;\r
8057   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8058   for (i = 0; i < N_BUTTONS; i++) {\r
8059     if (buttonDesc[i].hwnd != NULL)\r
8060       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8061   }\r
8062   CommentPopDown();\r
8063 }\r
8064 \r
8065 VOID SetTrainingModeOff()\r
8066 {\r
8067   int i;\r
8068   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8069   for (i = 0; i < N_BUTTONS; i++) {\r
8070     if (buttonDesc[i].hwnd != NULL)\r
8071       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8072   }\r
8073 }\r
8074 \r
8075 \r
8076 VOID\r
8077 SetUserThinkingEnables()\r
8078 {\r
8079   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8080 }\r
8081 \r
8082 VOID\r
8083 SetMachineThinkingEnables()\r
8084 {\r
8085   HMENU hMenu = GetMenu(hwndMain);\r
8086   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8087 \r
8088   SetMenuEnables(hMenu, machineThinkingEnables);\r
8089 \r
8090   if (gameMode == MachinePlaysBlack) {\r
8091     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8092   } else if (gameMode == MachinePlaysWhite) {\r
8093     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8094   } else if (gameMode == TwoMachinesPlay) {\r
8095     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8096   }\r
8097 }\r
8098 \r
8099 \r
8100 VOID\r
8101 DisplayTitle(char *str)\r
8102 {\r
8103   char title[MSG_SIZ], *host;\r
8104   if (str[0] != NULLCHAR) {\r
8105     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8106   } else if (appData.icsActive) {\r
8107     if (appData.icsCommPort[0] != NULLCHAR)\r
8108       host = "ICS";\r
8109     else \r
8110       host = appData.icsHost;\r
8111       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8112   } else if (appData.noChessProgram) {\r
8113     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8114   } else {\r
8115     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8116     strcat(title, ": ");\r
8117     strcat(title, first.tidy);\r
8118   }\r
8119   SetWindowText(hwndMain, title);\r
8120 }\r
8121 \r
8122 \r
8123 VOID\r
8124 DisplayMessage(char *str1, char *str2)\r
8125 {\r
8126   HDC hdc;\r
8127   HFONT oldFont;\r
8128   int remain = MESSAGE_TEXT_MAX - 1;\r
8129   int len;\r
8130 \r
8131   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8132   messageText[0] = NULLCHAR;\r
8133   if (*str1) {\r
8134     len = strlen(str1);\r
8135     if (len > remain) len = remain;\r
8136     strncpy(messageText, str1, len);\r
8137     messageText[len] = NULLCHAR;\r
8138     remain -= len;\r
8139   }\r
8140   if (*str2 && remain >= 2) {\r
8141     if (*str1) {\r
8142       strcat(messageText, "  ");\r
8143       remain -= 2;\r
8144     }\r
8145     len = strlen(str2);\r
8146     if (len > remain) len = remain;\r
8147     strncat(messageText, str2, len);\r
8148   }\r
8149   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8150   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8151 \r
8152   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8153 \r
8154   SAYMACHINEMOVE();\r
8155 \r
8156   hdc = GetDC(hwndMain);\r
8157   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8158   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8159              &messageRect, messageText, strlen(messageText), NULL);\r
8160   (void) SelectObject(hdc, oldFont);\r
8161   (void) ReleaseDC(hwndMain, hdc);\r
8162 }\r
8163 \r
8164 VOID\r
8165 DisplayError(char *str, int error)\r
8166 {\r
8167   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8168   int len;\r
8169 \r
8170   if (error == 0) {\r
8171     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8172   } else {\r
8173     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8174                         NULL, error, LANG_NEUTRAL,\r
8175                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8176     if (len > 0) {\r
8177       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8178     } else {\r
8179       ErrorMap *em = errmap;\r
8180       while (em->err != 0 && em->err != error) em++;\r
8181       if (em->err != 0) {\r
8182         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8183       } else {\r
8184         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8185       }\r
8186     }\r
8187   }\r
8188   \r
8189   ErrorPopUp(_("Error"), buf);\r
8190 }\r
8191 \r
8192 \r
8193 VOID\r
8194 DisplayMoveError(char *str)\r
8195 {\r
8196   fromX = fromY = -1;\r
8197   ClearHighlights();\r
8198   DrawPosition(FALSE, NULL);\r
8199   if (appData.popupMoveErrors) {\r
8200     ErrorPopUp(_("Error"), str);\r
8201   } else {\r
8202     DisplayMessage(str, "");\r
8203     moveErrorMessageUp = TRUE;\r
8204   }\r
8205 }\r
8206 \r
8207 VOID\r
8208 DisplayFatalError(char *str, int error, int exitStatus)\r
8209 {\r
8210   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8211   int len;\r
8212   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8213 \r
8214   if (error != 0) {\r
8215     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8216                         NULL, error, LANG_NEUTRAL,\r
8217                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8218     if (len > 0) {\r
8219       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8220     } else {\r
8221       ErrorMap *em = errmap;\r
8222       while (em->err != 0 && em->err != error) em++;\r
8223       if (em->err != 0) {\r
8224         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8225       } else {\r
8226         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8227       }\r
8228     }\r
8229     str = buf;\r
8230   }\r
8231   if (appData.debugMode) {\r
8232     fprintf(debugFP, "%s: %s\n", label, str);\r
8233   }\r
8234   if (appData.popupExitMessage) {\r
8235     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8236                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8237   }\r
8238   ExitEvent(exitStatus);\r
8239 }\r
8240 \r
8241 \r
8242 VOID\r
8243 DisplayInformation(char *str)\r
8244 {\r
8245   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8246 }\r
8247 \r
8248 \r
8249 VOID\r
8250 DisplayNote(char *str)\r
8251 {\r
8252   ErrorPopUp(_("Note"), str);\r
8253 }\r
8254 \r
8255 \r
8256 typedef struct {\r
8257   char *title, *question, *replyPrefix;\r
8258   ProcRef pr;\r
8259 } QuestionParams;\r
8260 \r
8261 LRESULT CALLBACK\r
8262 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8263 {\r
8264   static QuestionParams *qp;\r
8265   char reply[MSG_SIZ];\r
8266   int len, err;\r
8267 \r
8268   switch (message) {\r
8269   case WM_INITDIALOG:\r
8270     qp = (QuestionParams *) lParam;\r
8271     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8272     Translate(hDlg, DLG_Question);\r
8273     SetWindowText(hDlg, qp->title);\r
8274     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8275     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8276     return FALSE;\r
8277 \r
8278   case WM_COMMAND:\r
8279     switch (LOWORD(wParam)) {\r
8280     case IDOK:\r
8281       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8282       if (*reply) strcat(reply, " ");\r
8283       len = strlen(reply);\r
8284       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8285       strcat(reply, "\n");\r
8286       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8287       EndDialog(hDlg, TRUE);\r
8288       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8289       return TRUE;\r
8290     case IDCANCEL:\r
8291       EndDialog(hDlg, FALSE);\r
8292       return TRUE;\r
8293     default:\r
8294       break;\r
8295     }\r
8296     break;\r
8297   }\r
8298   return FALSE;\r
8299 }\r
8300 \r
8301 VOID\r
8302 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8303 {\r
8304     QuestionParams qp;\r
8305     FARPROC lpProc;\r
8306     \r
8307     qp.title = title;\r
8308     qp.question = question;\r
8309     qp.replyPrefix = replyPrefix;\r
8310     qp.pr = pr;\r
8311     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8312     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8313       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8314     FreeProcInstance(lpProc);\r
8315 }\r
8316 \r
8317 /* [AS] Pick FRC position */\r
8318 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8319 {\r
8320     static int * lpIndexFRC;\r
8321     BOOL index_is_ok;\r
8322     char buf[16];\r
8323 \r
8324     switch( message )\r
8325     {\r
8326     case WM_INITDIALOG:\r
8327         lpIndexFRC = (int *) lParam;\r
8328 \r
8329         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8330         Translate(hDlg, DLG_NewGameFRC);\r
8331 \r
8332         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8333         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8334         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8335         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8336 \r
8337         break;\r
8338 \r
8339     case WM_COMMAND:\r
8340         switch( LOWORD(wParam) ) {\r
8341         case IDOK:\r
8342             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8343             EndDialog( hDlg, 0 );\r
8344             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8345             return TRUE;\r
8346         case IDCANCEL:\r
8347             EndDialog( hDlg, 1 );   \r
8348             return TRUE;\r
8349         case IDC_NFG_Edit:\r
8350             if( HIWORD(wParam) == EN_CHANGE ) {\r
8351                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8352 \r
8353                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8354             }\r
8355             return TRUE;\r
8356         case IDC_NFG_Random:\r
8357           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8358             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8359             return TRUE;\r
8360         }\r
8361 \r
8362         break;\r
8363     }\r
8364 \r
8365     return FALSE;\r
8366 }\r
8367 \r
8368 int NewGameFRC()\r
8369 {\r
8370     int result;\r
8371     int index = appData.defaultFrcPosition;\r
8372     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8373 \r
8374     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8375 \r
8376     if( result == 0 ) {\r
8377         appData.defaultFrcPosition = index;\r
8378     }\r
8379 \r
8380     return result;\r
8381 }\r
8382 \r
8383 /* [AS] Game list options. Refactored by HGM */\r
8384 \r
8385 HWND gameListOptionsDialog;\r
8386 \r
8387 // low-level front-end: clear text edit / list widget\r
8388 void\r
8389 GLT_ClearList()\r
8390 {\r
8391     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8392 }\r
8393 \r
8394 // low-level front-end: clear text edit / list widget\r
8395 void\r
8396 GLT_DeSelectList()\r
8397 {\r
8398     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8399 }\r
8400 \r
8401 // low-level front-end: append line to text edit / list widget\r
8402 void\r
8403 GLT_AddToList( char *name )\r
8404 {\r
8405     if( name != 0 ) {\r
8406             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8407     }\r
8408 }\r
8409 \r
8410 // low-level front-end: get line from text edit / list widget\r
8411 Boolean\r
8412 GLT_GetFromList( int index, char *name )\r
8413 {\r
8414     if( name != 0 ) {\r
8415             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8416                 return TRUE;\r
8417     }\r
8418     return FALSE;\r
8419 }\r
8420 \r
8421 void GLT_MoveSelection( HWND hDlg, int delta )\r
8422 {\r
8423     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8424     int idx2 = idx1 + delta;\r
8425     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8426 \r
8427     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8428         char buf[128];\r
8429 \r
8430         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8431         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8432         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8433         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8434     }\r
8435 }\r
8436 \r
8437 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8438 {\r
8439     switch( message )\r
8440     {\r
8441     case WM_INITDIALOG:\r
8442         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8443         \r
8444         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8445         Translate(hDlg, DLG_GameListOptions);\r
8446 \r
8447         /* Initialize list */\r
8448         GLT_TagsToList( lpUserGLT );\r
8449 \r
8450         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8451 \r
8452         break;\r
8453 \r
8454     case WM_COMMAND:\r
8455         switch( LOWORD(wParam) ) {\r
8456         case IDOK:\r
8457             GLT_ParseList();\r
8458             EndDialog( hDlg, 0 );\r
8459             return TRUE;\r
8460         case IDCANCEL:\r
8461             EndDialog( hDlg, 1 );\r
8462             return TRUE;\r
8463 \r
8464         case IDC_GLT_Default:\r
8465             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8466             return TRUE;\r
8467 \r
8468         case IDC_GLT_Restore:\r
8469             GLT_TagsToList( appData.gameListTags );\r
8470             return TRUE;\r
8471 \r
8472         case IDC_GLT_Up:\r
8473             GLT_MoveSelection( hDlg, -1 );\r
8474             return TRUE;\r
8475 \r
8476         case IDC_GLT_Down:\r
8477             GLT_MoveSelection( hDlg, +1 );\r
8478             return TRUE;\r
8479         }\r
8480 \r
8481         break;\r
8482     }\r
8483 \r
8484     return FALSE;\r
8485 }\r
8486 \r
8487 int GameListOptions()\r
8488 {\r
8489     int result;\r
8490     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8491 \r
8492       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8493 \r
8494     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8495 \r
8496     if( result == 0 ) {\r
8497         /* [AS] Memory leak here! */\r
8498         appData.gameListTags = strdup( lpUserGLT ); \r
8499     }\r
8500 \r
8501     return result;\r
8502 }\r
8503 \r
8504 VOID\r
8505 DisplayIcsInteractionTitle(char *str)\r
8506 {\r
8507   char consoleTitle[MSG_SIZ];\r
8508 \r
8509     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8510   SetWindowText(hwndConsole, consoleTitle);\r
8511 }\r
8512 \r
8513 void\r
8514 DrawPosition(int fullRedraw, Board board)\r
8515 {\r
8516   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8517 }\r
8518 \r
8519 void NotifyFrontendLogin()\r
8520 {\r
8521         if (hwndConsole)\r
8522                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8523 }\r
8524 \r
8525 VOID\r
8526 ResetFrontEnd()\r
8527 {\r
8528   fromX = fromY = -1;\r
8529   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8530     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8531     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8532     dragInfo.lastpos = dragInfo.pos;\r
8533     dragInfo.start.x = dragInfo.start.y = -1;\r
8534     dragInfo.from = dragInfo.start;\r
8535     ReleaseCapture();\r
8536     DrawPosition(TRUE, NULL);\r
8537   }\r
8538   TagsPopDown();\r
8539 }\r
8540 \r
8541 \r
8542 VOID\r
8543 CommentPopUp(char *title, char *str)\r
8544 {\r
8545   HWND hwnd = GetActiveWindow();\r
8546   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8547   SAY(str);\r
8548   SetActiveWindow(hwnd);\r
8549 }\r
8550 \r
8551 VOID\r
8552 CommentPopDown(void)\r
8553 {\r
8554   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8555   if (commentDialog) {\r
8556     ShowWindow(commentDialog, SW_HIDE);\r
8557   }\r
8558   commentUp = FALSE;\r
8559 }\r
8560 \r
8561 VOID\r
8562 EditCommentPopUp(int index, char *title, char *str)\r
8563 {\r
8564   EitherCommentPopUp(index, title, str, TRUE);\r
8565 }\r
8566 \r
8567 \r
8568 VOID\r
8569 RingBell()\r
8570 {\r
8571   MyPlaySound(&sounds[(int)SoundMove]);\r
8572 }\r
8573 \r
8574 VOID PlayIcsWinSound()\r
8575 {\r
8576   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8577 }\r
8578 \r
8579 VOID PlayIcsLossSound()\r
8580 {\r
8581   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8582 }\r
8583 \r
8584 VOID PlayIcsDrawSound()\r
8585 {\r
8586   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8587 }\r
8588 \r
8589 VOID PlayIcsUnfinishedSound()\r
8590 {\r
8591   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8592 }\r
8593 \r
8594 VOID\r
8595 PlayAlarmSound()\r
8596 {\r
8597   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8598 }\r
8599 \r
8600 \r
8601 VOID\r
8602 EchoOn()\r
8603 {\r
8604   HWND hInput;\r
8605   consoleEcho = TRUE;\r
8606   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8607   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8608   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8609 }\r
8610 \r
8611 \r
8612 VOID\r
8613 EchoOff()\r
8614 {\r
8615   CHARFORMAT cf;\r
8616   HWND hInput;\r
8617   consoleEcho = FALSE;\r
8618   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8619   /* This works OK: set text and background both to the same color */\r
8620   cf = consoleCF;\r
8621   cf.crTextColor = COLOR_ECHOOFF;\r
8622   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8623   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8624 }\r
8625 \r
8626 /* No Raw()...? */\r
8627 \r
8628 void Colorize(ColorClass cc, int continuation)\r
8629 {\r
8630   currentColorClass = cc;\r
8631   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8632   consoleCF.crTextColor = textAttribs[cc].color;\r
8633   consoleCF.dwEffects = textAttribs[cc].effects;\r
8634   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8635 }\r
8636 \r
8637 char *\r
8638 UserName()\r
8639 {\r
8640   static char buf[MSG_SIZ];\r
8641   DWORD bufsiz = MSG_SIZ;\r
8642 \r
8643   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8644         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8645   }\r
8646   if (!GetUserName(buf, &bufsiz)) {\r
8647     /*DisplayError("Error getting user name", GetLastError());*/\r
8648     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8649   }\r
8650   return buf;\r
8651 }\r
8652 \r
8653 char *\r
8654 HostName()\r
8655 {\r
8656   static char buf[MSG_SIZ];\r
8657   DWORD bufsiz = MSG_SIZ;\r
8658 \r
8659   if (!GetComputerName(buf, &bufsiz)) {\r
8660     /*DisplayError("Error getting host name", GetLastError());*/\r
8661     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8662   }\r
8663   return buf;\r
8664 }\r
8665 \r
8666 \r
8667 int\r
8668 ClockTimerRunning()\r
8669 {\r
8670   return clockTimerEvent != 0;\r
8671 }\r
8672 \r
8673 int\r
8674 StopClockTimer()\r
8675 {\r
8676   if (clockTimerEvent == 0) return FALSE;\r
8677   KillTimer(hwndMain, clockTimerEvent);\r
8678   clockTimerEvent = 0;\r
8679   return TRUE;\r
8680 }\r
8681 \r
8682 void\r
8683 StartClockTimer(long millisec)\r
8684 {\r
8685   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8686                              (UINT) millisec, NULL);\r
8687 }\r
8688 \r
8689 void\r
8690 DisplayWhiteClock(long timeRemaining, int highlight)\r
8691 {\r
8692   HDC hdc;\r
8693   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8694 \r
8695   if(appData.noGUI) return;\r
8696   hdc = GetDC(hwndMain);\r
8697   if (!IsIconic(hwndMain)) {\r
8698     DisplayAClock(hdc, timeRemaining, highlight, \r
8699                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8700   }\r
8701   if (highlight && iconCurrent == iconBlack) {\r
8702     iconCurrent = iconWhite;\r
8703     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8704     if (IsIconic(hwndMain)) {\r
8705       DrawIcon(hdc, 2, 2, iconCurrent);\r
8706     }\r
8707   }\r
8708   (void) ReleaseDC(hwndMain, hdc);\r
8709   if (hwndConsole)\r
8710     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8711 }\r
8712 \r
8713 void\r
8714 DisplayBlackClock(long timeRemaining, int highlight)\r
8715 {\r
8716   HDC hdc;\r
8717   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8718 \r
8719   if(appData.noGUI) return;\r
8720   hdc = GetDC(hwndMain);\r
8721   if (!IsIconic(hwndMain)) {\r
8722     DisplayAClock(hdc, timeRemaining, highlight, \r
8723                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8724   }\r
8725   if (highlight && iconCurrent == iconWhite) {\r
8726     iconCurrent = iconBlack;\r
8727     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8728     if (IsIconic(hwndMain)) {\r
8729       DrawIcon(hdc, 2, 2, iconCurrent);\r
8730     }\r
8731   }\r
8732   (void) ReleaseDC(hwndMain, hdc);\r
8733   if (hwndConsole)\r
8734     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8735 }\r
8736 \r
8737 \r
8738 int\r
8739 LoadGameTimerRunning()\r
8740 {\r
8741   return loadGameTimerEvent != 0;\r
8742 }\r
8743 \r
8744 int\r
8745 StopLoadGameTimer()\r
8746 {\r
8747   if (loadGameTimerEvent == 0) return FALSE;\r
8748   KillTimer(hwndMain, loadGameTimerEvent);\r
8749   loadGameTimerEvent = 0;\r
8750   return TRUE;\r
8751 }\r
8752 \r
8753 void\r
8754 StartLoadGameTimer(long millisec)\r
8755 {\r
8756   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8757                                 (UINT) millisec, NULL);\r
8758 }\r
8759 \r
8760 void\r
8761 AutoSaveGame()\r
8762 {\r
8763   char *defName;\r
8764   FILE *f;\r
8765   char fileTitle[MSG_SIZ];\r
8766 \r
8767   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8768   f = OpenFileDialog(hwndMain, "a", defName,\r
8769                      appData.oldSaveStyle ? "gam" : "pgn",\r
8770                      GAME_FILT, \r
8771                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8772   if (f != NULL) {\r
8773     SaveGame(f, 0, "");\r
8774     fclose(f);\r
8775   }\r
8776 }\r
8777 \r
8778 \r
8779 void\r
8780 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8781 {\r
8782   if (delayedTimerEvent != 0) {\r
8783     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8784       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8785     }\r
8786     KillTimer(hwndMain, delayedTimerEvent);\r
8787     delayedTimerEvent = 0;\r
8788     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8789     delayedTimerCallback();\r
8790   }\r
8791   delayedTimerCallback = cb;\r
8792   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8793                                 (UINT) millisec, NULL);\r
8794 }\r
8795 \r
8796 DelayedEventCallback\r
8797 GetDelayedEvent()\r
8798 {\r
8799   if (delayedTimerEvent) {\r
8800     return delayedTimerCallback;\r
8801   } else {\r
8802     return NULL;\r
8803   }\r
8804 }\r
8805 \r
8806 void\r
8807 CancelDelayedEvent()\r
8808 {\r
8809   if (delayedTimerEvent) {\r
8810     KillTimer(hwndMain, delayedTimerEvent);\r
8811     delayedTimerEvent = 0;\r
8812   }\r
8813 }\r
8814 \r
8815 DWORD GetWin32Priority(int nice)\r
8816 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8817 /*\r
8818 REALTIME_PRIORITY_CLASS     0x00000100\r
8819 HIGH_PRIORITY_CLASS         0x00000080\r
8820 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8821 NORMAL_PRIORITY_CLASS       0x00000020\r
8822 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8823 IDLE_PRIORITY_CLASS         0x00000040\r
8824 */\r
8825         if (nice < -15) return 0x00000080;\r
8826         if (nice < 0)   return 0x00008000;\r
8827         if (nice == 0)  return 0x00000020;\r
8828         if (nice < 15)  return 0x00004000;\r
8829         return 0x00000040;\r
8830 }\r
8831 \r
8832 /* Start a child process running the given program.\r
8833    The process's standard output can be read from "from", and its\r
8834    standard input can be written to "to".\r
8835    Exit with fatal error if anything goes wrong.\r
8836    Returns an opaque pointer that can be used to destroy the process\r
8837    later.\r
8838 */\r
8839 int\r
8840 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8841 {\r
8842 #define BUFSIZE 4096\r
8843 \r
8844   HANDLE hChildStdinRd, hChildStdinWr,\r
8845     hChildStdoutRd, hChildStdoutWr;\r
8846   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8847   SECURITY_ATTRIBUTES saAttr;\r
8848   BOOL fSuccess;\r
8849   PROCESS_INFORMATION piProcInfo;\r
8850   STARTUPINFO siStartInfo;\r
8851   ChildProc *cp;\r
8852   char buf[MSG_SIZ];\r
8853   DWORD err;\r
8854 \r
8855   if (appData.debugMode) {\r
8856     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8857   }\r
8858 \r
8859   *pr = NoProc;\r
8860 \r
8861   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8862   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8863   saAttr.bInheritHandle = TRUE;\r
8864   saAttr.lpSecurityDescriptor = NULL;\r
8865 \r
8866   /*\r
8867    * The steps for redirecting child's STDOUT:\r
8868    *     1. Create anonymous pipe to be STDOUT for child.\r
8869    *     2. Create a noninheritable duplicate of read handle,\r
8870    *         and close the inheritable read handle.\r
8871    */\r
8872 \r
8873   /* Create a pipe for the child's STDOUT. */\r
8874   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8875     return GetLastError();\r
8876   }\r
8877 \r
8878   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8879   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8880                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8881                              FALSE,     /* not inherited */\r
8882                              DUPLICATE_SAME_ACCESS);\r
8883   if (! fSuccess) {\r
8884     return GetLastError();\r
8885   }\r
8886   CloseHandle(hChildStdoutRd);\r
8887 \r
8888   /*\r
8889    * The steps for redirecting child's STDIN:\r
8890    *     1. Create anonymous pipe to be STDIN for child.\r
8891    *     2. Create a noninheritable duplicate of write handle,\r
8892    *         and close the inheritable write handle.\r
8893    */\r
8894 \r
8895   /* Create a pipe for the child's STDIN. */\r
8896   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8897     return GetLastError();\r
8898   }\r
8899 \r
8900   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8901   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8902                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8903                              FALSE,     /* not inherited */\r
8904                              DUPLICATE_SAME_ACCESS);\r
8905   if (! fSuccess) {\r
8906     return GetLastError();\r
8907   }\r
8908   CloseHandle(hChildStdinWr);\r
8909 \r
8910   /* Arrange to (1) look in dir for the child .exe file, and\r
8911    * (2) have dir be the child's working directory.  Interpret\r
8912    * dir relative to the directory WinBoard loaded from. */\r
8913   GetCurrentDirectory(MSG_SIZ, buf);\r
8914   SetCurrentDirectory(installDir);\r
8915   // kludgey way to update logos in tourney, as long as back-end can't do it\r
8916   if(!strcmp(cmdLine, first.program)) LoadLogo(&first, 0); else\r
8917   if(!strcmp(cmdLine, second.program)) LoadLogo(&second, 1);\r
8918   SetCurrentDirectory(dir);\r
8919 \r
8920   /* Now create the child process. */\r
8921 \r
8922   siStartInfo.cb = sizeof(STARTUPINFO);\r
8923   siStartInfo.lpReserved = NULL;\r
8924   siStartInfo.lpDesktop = NULL;\r
8925   siStartInfo.lpTitle = NULL;\r
8926   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8927   siStartInfo.cbReserved2 = 0;\r
8928   siStartInfo.lpReserved2 = NULL;\r
8929   siStartInfo.hStdInput = hChildStdinRd;\r
8930   siStartInfo.hStdOutput = hChildStdoutWr;\r
8931   siStartInfo.hStdError = hChildStdoutWr;\r
8932 \r
8933   fSuccess = CreateProcess(NULL,\r
8934                            cmdLine,        /* command line */\r
8935                            NULL,           /* process security attributes */\r
8936                            NULL,           /* primary thread security attrs */\r
8937                            TRUE,           /* handles are inherited */\r
8938                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8939                            NULL,           /* use parent's environment */\r
8940                            NULL,\r
8941                            &siStartInfo, /* STARTUPINFO pointer */\r
8942                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8943 \r
8944   err = GetLastError();\r
8945   SetCurrentDirectory(buf); /* return to prev directory */\r
8946   if (! fSuccess) {\r
8947     return err;\r
8948   }\r
8949 \r
8950   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8951     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8952     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8953   }\r
8954 \r
8955   /* Close the handles we don't need in the parent */\r
8956   CloseHandle(piProcInfo.hThread);\r
8957   CloseHandle(hChildStdinRd);\r
8958   CloseHandle(hChildStdoutWr);\r
8959 \r
8960   /* Prepare return value */\r
8961   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8962   cp->kind = CPReal;\r
8963   cp->hProcess = piProcInfo.hProcess;\r
8964   cp->pid = piProcInfo.dwProcessId;\r
8965   cp->hFrom = hChildStdoutRdDup;\r
8966   cp->hTo = hChildStdinWrDup;\r
8967 \r
8968   *pr = (void *) cp;\r
8969 \r
8970   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8971      2000 where engines sometimes don't see the initial command(s)\r
8972      from WinBoard and hang.  I don't understand how that can happen,\r
8973      but the Sleep is harmless, so I've put it in.  Others have also\r
8974      reported what may be the same problem, so hopefully this will fix\r
8975      it for them too.  */\r
8976   Sleep(500);\r
8977 \r
8978   return NO_ERROR;\r
8979 }\r
8980 \r
8981 \r
8982 void\r
8983 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8984 {\r
8985   ChildProc *cp; int result;\r
8986 \r
8987   cp = (ChildProc *) pr;\r
8988   if (cp == NULL) return;\r
8989 \r
8990   switch (cp->kind) {\r
8991   case CPReal:\r
8992     /* TerminateProcess is considered harmful, so... */\r
8993     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8994     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8995     /* The following doesn't work because the chess program\r
8996        doesn't "have the same console" as WinBoard.  Maybe\r
8997        we could arrange for this even though neither WinBoard\r
8998        nor the chess program uses a console for stdio? */\r
8999     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9000 \r
9001     /* [AS] Special termination modes for misbehaving programs... */\r
9002     if( signal == 9 ) { \r
9003         result = TerminateProcess( cp->hProcess, 0 );\r
9004 \r
9005         if ( appData.debugMode) {\r
9006             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9007         }\r
9008     }\r
9009     else if( signal == 10 ) {\r
9010         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9011 \r
9012         if( dw != WAIT_OBJECT_0 ) {\r
9013             result = TerminateProcess( cp->hProcess, 0 );\r
9014 \r
9015             if ( appData.debugMode) {\r
9016                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9017             }\r
9018 \r
9019         }\r
9020     }\r
9021 \r
9022     CloseHandle(cp->hProcess);\r
9023     break;\r
9024 \r
9025   case CPComm:\r
9026     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9027     break;\r
9028 \r
9029   case CPSock:\r
9030     closesocket(cp->sock);\r
9031     WSACleanup();\r
9032     break;\r
9033 \r
9034   case CPRcmd:\r
9035     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9036     closesocket(cp->sock);\r
9037     closesocket(cp->sock2);\r
9038     WSACleanup();\r
9039     break;\r
9040   }\r
9041   free(cp);\r
9042 }\r
9043 \r
9044 void\r
9045 InterruptChildProcess(ProcRef pr)\r
9046 {\r
9047   ChildProc *cp;\r
9048 \r
9049   cp = (ChildProc *) pr;\r
9050   if (cp == NULL) return;\r
9051   switch (cp->kind) {\r
9052   case CPReal:\r
9053     /* The following doesn't work because the chess program\r
9054        doesn't "have the same console" as WinBoard.  Maybe\r
9055        we could arrange for this even though neither WinBoard\r
9056        nor the chess program uses a console for stdio */\r
9057     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9058     break;\r
9059 \r
9060   case CPComm:\r
9061   case CPSock:\r
9062     /* Can't interrupt */\r
9063     break;\r
9064 \r
9065   case CPRcmd:\r
9066     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9067     break;\r
9068   }\r
9069 }\r
9070 \r
9071 \r
9072 int\r
9073 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9074 {\r
9075   char cmdLine[MSG_SIZ];\r
9076 \r
9077   if (port[0] == NULLCHAR) {\r
9078     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9079   } else {\r
9080     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9081   }\r
9082   return StartChildProcess(cmdLine, "", pr);\r
9083 }\r
9084 \r
9085 \r
9086 /* Code to open TCP sockets */\r
9087 \r
9088 int\r
9089 OpenTCP(char *host, char *port, ProcRef *pr)\r
9090 {\r
9091   ChildProc *cp;\r
9092   int err;\r
9093   SOCKET s;\r
9094   struct sockaddr_in sa, mysa;\r
9095   struct hostent FAR *hp;\r
9096   unsigned short uport;\r
9097   WORD wVersionRequested;\r
9098   WSADATA wsaData;\r
9099 \r
9100   /* Initialize socket DLL */\r
9101   wVersionRequested = MAKEWORD(1, 1);\r
9102   err = WSAStartup(wVersionRequested, &wsaData);\r
9103   if (err != 0) return err;\r
9104 \r
9105   /* Make socket */\r
9106   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9107     err = WSAGetLastError();\r
9108     WSACleanup();\r
9109     return err;\r
9110   }\r
9111 \r
9112   /* Bind local address using (mostly) don't-care values.\r
9113    */\r
9114   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9115   mysa.sin_family = AF_INET;\r
9116   mysa.sin_addr.s_addr = INADDR_ANY;\r
9117   uport = (unsigned short) 0;\r
9118   mysa.sin_port = htons(uport);\r
9119   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9120       == SOCKET_ERROR) {\r
9121     err = WSAGetLastError();\r
9122     WSACleanup();\r
9123     return err;\r
9124   }\r
9125 \r
9126   /* Resolve remote host name */\r
9127   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9128   if (!(hp = gethostbyname(host))) {\r
9129     unsigned int b0, b1, b2, b3;\r
9130 \r
9131     err = WSAGetLastError();\r
9132 \r
9133     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9134       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9135       hp->h_addrtype = AF_INET;\r
9136       hp->h_length = 4;\r
9137       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9138       hp->h_addr_list[0] = (char *) malloc(4);\r
9139       hp->h_addr_list[0][0] = (char) b0;\r
9140       hp->h_addr_list[0][1] = (char) b1;\r
9141       hp->h_addr_list[0][2] = (char) b2;\r
9142       hp->h_addr_list[0][3] = (char) b3;\r
9143     } else {\r
9144       WSACleanup();\r
9145       return err;\r
9146     }\r
9147   }\r
9148   sa.sin_family = hp->h_addrtype;\r
9149   uport = (unsigned short) atoi(port);\r
9150   sa.sin_port = htons(uport);\r
9151   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9152 \r
9153   /* Make connection */\r
9154   if (connect(s, (struct sockaddr *) &sa,\r
9155               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9156     err = WSAGetLastError();\r
9157     WSACleanup();\r
9158     return err;\r
9159   }\r
9160 \r
9161   /* Prepare return value */\r
9162   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9163   cp->kind = CPSock;\r
9164   cp->sock = s;\r
9165   *pr = (ProcRef *) cp;\r
9166 \r
9167   return NO_ERROR;\r
9168 }\r
9169 \r
9170 int\r
9171 OpenCommPort(char *name, ProcRef *pr)\r
9172 {\r
9173   HANDLE h;\r
9174   COMMTIMEOUTS ct;\r
9175   ChildProc *cp;\r
9176   char fullname[MSG_SIZ];\r
9177 \r
9178   if (*name != '\\')\r
9179     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9180   else\r
9181     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9182 \r
9183   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9184                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9185   if (h == (HANDLE) -1) {\r
9186     return GetLastError();\r
9187   }\r
9188   hCommPort = h;\r
9189 \r
9190   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9191 \r
9192   /* Accumulate characters until a 100ms pause, then parse */\r
9193   ct.ReadIntervalTimeout = 100;\r
9194   ct.ReadTotalTimeoutMultiplier = 0;\r
9195   ct.ReadTotalTimeoutConstant = 0;\r
9196   ct.WriteTotalTimeoutMultiplier = 0;\r
9197   ct.WriteTotalTimeoutConstant = 0;\r
9198   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9199 \r
9200   /* Prepare return value */\r
9201   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9202   cp->kind = CPComm;\r
9203   cp->hFrom = h;\r
9204   cp->hTo = h;\r
9205   *pr = (ProcRef *) cp;\r
9206 \r
9207   return NO_ERROR;\r
9208 }\r
9209 \r
9210 int\r
9211 OpenLoopback(ProcRef *pr)\r
9212 {\r
9213   DisplayFatalError(_("Not implemented"), 0, 1);\r
9214   return NO_ERROR;\r
9215 }\r
9216 \r
9217 \r
9218 int\r
9219 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9220 {\r
9221   ChildProc *cp;\r
9222   int err;\r
9223   SOCKET s, s2, s3;\r
9224   struct sockaddr_in sa, mysa;\r
9225   struct hostent FAR *hp;\r
9226   unsigned short uport;\r
9227   WORD wVersionRequested;\r
9228   WSADATA wsaData;\r
9229   int fromPort;\r
9230   char stderrPortStr[MSG_SIZ];\r
9231 \r
9232   /* Initialize socket DLL */\r
9233   wVersionRequested = MAKEWORD(1, 1);\r
9234   err = WSAStartup(wVersionRequested, &wsaData);\r
9235   if (err != 0) return err;\r
9236 \r
9237   /* Resolve remote host name */\r
9238   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9239   if (!(hp = gethostbyname(host))) {\r
9240     unsigned int b0, b1, b2, b3;\r
9241 \r
9242     err = WSAGetLastError();\r
9243 \r
9244     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9245       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9246       hp->h_addrtype = AF_INET;\r
9247       hp->h_length = 4;\r
9248       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9249       hp->h_addr_list[0] = (char *) malloc(4);\r
9250       hp->h_addr_list[0][0] = (char) b0;\r
9251       hp->h_addr_list[0][1] = (char) b1;\r
9252       hp->h_addr_list[0][2] = (char) b2;\r
9253       hp->h_addr_list[0][3] = (char) b3;\r
9254     } else {\r
9255       WSACleanup();\r
9256       return err;\r
9257     }\r
9258   }\r
9259   sa.sin_family = hp->h_addrtype;\r
9260   uport = (unsigned short) 514;\r
9261   sa.sin_port = htons(uport);\r
9262   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9263 \r
9264   /* Bind local socket to unused "privileged" port address\r
9265    */\r
9266   s = INVALID_SOCKET;\r
9267   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9268   mysa.sin_family = AF_INET;\r
9269   mysa.sin_addr.s_addr = INADDR_ANY;\r
9270   for (fromPort = 1023;; fromPort--) {\r
9271     if (fromPort < 0) {\r
9272       WSACleanup();\r
9273       return WSAEADDRINUSE;\r
9274     }\r
9275     if (s == INVALID_SOCKET) {\r
9276       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9277         err = WSAGetLastError();\r
9278         WSACleanup();\r
9279         return err;\r
9280       }\r
9281     }\r
9282     uport = (unsigned short) fromPort;\r
9283     mysa.sin_port = htons(uport);\r
9284     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9285         == SOCKET_ERROR) {\r
9286       err = WSAGetLastError();\r
9287       if (err == WSAEADDRINUSE) continue;\r
9288       WSACleanup();\r
9289       return err;\r
9290     }\r
9291     if (connect(s, (struct sockaddr *) &sa,\r
9292       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9293       err = WSAGetLastError();\r
9294       if (err == WSAEADDRINUSE) {\r
9295         closesocket(s);\r
9296         s = -1;\r
9297         continue;\r
9298       }\r
9299       WSACleanup();\r
9300       return err;\r
9301     }\r
9302     break;\r
9303   }\r
9304 \r
9305   /* Bind stderr local socket to unused "privileged" port address\r
9306    */\r
9307   s2 = INVALID_SOCKET;\r
9308   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9309   mysa.sin_family = AF_INET;\r
9310   mysa.sin_addr.s_addr = INADDR_ANY;\r
9311   for (fromPort = 1023;; fromPort--) {\r
9312     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9313     if (fromPort < 0) {\r
9314       (void) closesocket(s);\r
9315       WSACleanup();\r
9316       return WSAEADDRINUSE;\r
9317     }\r
9318     if (s2 == INVALID_SOCKET) {\r
9319       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9320         err = WSAGetLastError();\r
9321         closesocket(s);\r
9322         WSACleanup();\r
9323         return err;\r
9324       }\r
9325     }\r
9326     uport = (unsigned short) fromPort;\r
9327     mysa.sin_port = htons(uport);\r
9328     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9329         == SOCKET_ERROR) {\r
9330       err = WSAGetLastError();\r
9331       if (err == WSAEADDRINUSE) continue;\r
9332       (void) closesocket(s);\r
9333       WSACleanup();\r
9334       return err;\r
9335     }\r
9336     if (listen(s2, 1) == SOCKET_ERROR) {\r
9337       err = WSAGetLastError();\r
9338       if (err == WSAEADDRINUSE) {\r
9339         closesocket(s2);\r
9340         s2 = INVALID_SOCKET;\r
9341         continue;\r
9342       }\r
9343       (void) closesocket(s);\r
9344       (void) closesocket(s2);\r
9345       WSACleanup();\r
9346       return err;\r
9347     }\r
9348     break;\r
9349   }\r
9350   prevStderrPort = fromPort; // remember port used\r
9351   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9352 \r
9353   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9354     err = WSAGetLastError();\r
9355     (void) closesocket(s);\r
9356     (void) closesocket(s2);\r
9357     WSACleanup();\r
9358     return err;\r
9359   }\r
9360 \r
9361   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9362     err = WSAGetLastError();\r
9363     (void) closesocket(s);\r
9364     (void) closesocket(s2);\r
9365     WSACleanup();\r
9366     return err;\r
9367   }\r
9368   if (*user == NULLCHAR) user = UserName();\r
9369   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9370     err = WSAGetLastError();\r
9371     (void) closesocket(s);\r
9372     (void) closesocket(s2);\r
9373     WSACleanup();\r
9374     return err;\r
9375   }\r
9376   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9377     err = WSAGetLastError();\r
9378     (void) closesocket(s);\r
9379     (void) closesocket(s2);\r
9380     WSACleanup();\r
9381     return err;\r
9382   }\r
9383 \r
9384   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9385     err = WSAGetLastError();\r
9386     (void) closesocket(s);\r
9387     (void) closesocket(s2);\r
9388     WSACleanup();\r
9389     return err;\r
9390   }\r
9391   (void) closesocket(s2);  /* Stop listening */\r
9392 \r
9393   /* Prepare return value */\r
9394   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9395   cp->kind = CPRcmd;\r
9396   cp->sock = s;\r
9397   cp->sock2 = s3;\r
9398   *pr = (ProcRef *) cp;\r
9399 \r
9400   return NO_ERROR;\r
9401 }\r
9402 \r
9403 \r
9404 InputSourceRef\r
9405 AddInputSource(ProcRef pr, int lineByLine,\r
9406                InputCallback func, VOIDSTAR closure)\r
9407 {\r
9408   InputSource *is, *is2 = NULL;\r
9409   ChildProc *cp = (ChildProc *) pr;\r
9410 \r
9411   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9412   is->lineByLine = lineByLine;\r
9413   is->func = func;\r
9414   is->closure = closure;\r
9415   is->second = NULL;\r
9416   is->next = is->buf;\r
9417   if (pr == NoProc) {\r
9418     is->kind = CPReal;\r
9419     consoleInputSource = is;\r
9420   } else {\r
9421     is->kind = cp->kind;\r
9422     /* \r
9423         [AS] Try to avoid a race condition if the thread is given control too early:\r
9424         we create all threads suspended so that the is->hThread variable can be\r
9425         safely assigned, then let the threads start with ResumeThread.\r
9426     */\r
9427     switch (cp->kind) {\r
9428     case CPReal:\r
9429       is->hFile = cp->hFrom;\r
9430       cp->hFrom = NULL; /* now owned by InputThread */\r
9431       is->hThread =\r
9432         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9433                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9434       break;\r
9435 \r
9436     case CPComm:\r
9437       is->hFile = cp->hFrom;\r
9438       cp->hFrom = NULL; /* now owned by InputThread */\r
9439       is->hThread =\r
9440         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9441                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9442       break;\r
9443 \r
9444     case CPSock:\r
9445       is->sock = cp->sock;\r
9446       is->hThread =\r
9447         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9448                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9449       break;\r
9450 \r
9451     case CPRcmd:\r
9452       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9453       *is2 = *is;\r
9454       is->sock = cp->sock;\r
9455       is->second = is2;\r
9456       is2->sock = cp->sock2;\r
9457       is2->second = is2;\r
9458       is->hThread =\r
9459         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9460                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9461       is2->hThread =\r
9462         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9463                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9464       break;\r
9465     }\r
9466 \r
9467     if( is->hThread != NULL ) {\r
9468         ResumeThread( is->hThread );\r
9469     }\r
9470 \r
9471     if( is2 != NULL && is2->hThread != NULL ) {\r
9472         ResumeThread( is2->hThread );\r
9473     }\r
9474   }\r
9475 \r
9476   return (InputSourceRef) is;\r
9477 }\r
9478 \r
9479 void\r
9480 RemoveInputSource(InputSourceRef isr)\r
9481 {\r
9482   InputSource *is;\r
9483 \r
9484   is = (InputSource *) isr;\r
9485   is->hThread = NULL;  /* tell thread to stop */\r
9486   CloseHandle(is->hThread);\r
9487   if (is->second != NULL) {\r
9488     is->second->hThread = NULL;\r
9489     CloseHandle(is->second->hThread);\r
9490   }\r
9491 }\r
9492 \r
9493 int no_wrap(char *message, int count)\r
9494 {\r
9495     ConsoleOutput(message, count, FALSE);\r
9496     return count;\r
9497 }\r
9498 \r
9499 int\r
9500 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9501 {\r
9502   DWORD dOutCount;\r
9503   int outCount = SOCKET_ERROR;\r
9504   ChildProc *cp = (ChildProc *) pr;\r
9505   static OVERLAPPED ovl;\r
9506   static int line = 0;\r
9507 \r
9508   if (pr == NoProc)\r
9509   {\r
9510     if (appData.noJoin || !appData.useInternalWrap)\r
9511       return no_wrap(message, count);\r
9512     else\r
9513     {\r
9514       int width = get_term_width();\r
9515       int len = wrap(NULL, message, count, width, &line);\r
9516       char *msg = malloc(len);\r
9517       int dbgchk;\r
9518 \r
9519       if (!msg)\r
9520         return no_wrap(message, count);\r
9521       else\r
9522       {\r
9523         dbgchk = wrap(msg, message, count, width, &line);\r
9524         if (dbgchk != len && appData.debugMode)\r
9525             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9526         ConsoleOutput(msg, len, FALSE);\r
9527         free(msg);\r
9528         return len;\r
9529       }\r
9530     }\r
9531   }\r
9532 \r
9533   if (ovl.hEvent == NULL) {\r
9534     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9535   }\r
9536   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9537 \r
9538   switch (cp->kind) {\r
9539   case CPSock:\r
9540   case CPRcmd:\r
9541     outCount = send(cp->sock, message, count, 0);\r
9542     if (outCount == SOCKET_ERROR) {\r
9543       *outError = WSAGetLastError();\r
9544     } else {\r
9545       *outError = NO_ERROR;\r
9546     }\r
9547     break;\r
9548 \r
9549   case CPReal:\r
9550     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9551                   &dOutCount, NULL)) {\r
9552       *outError = NO_ERROR;\r
9553       outCount = (int) dOutCount;\r
9554     } else {\r
9555       *outError = GetLastError();\r
9556     }\r
9557     break;\r
9558 \r
9559   case CPComm:\r
9560     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9561                             &dOutCount, &ovl);\r
9562     if (*outError == NO_ERROR) {\r
9563       outCount = (int) dOutCount;\r
9564     }\r
9565     break;\r
9566   }\r
9567   return outCount;\r
9568 }\r
9569 \r
9570 int\r
9571 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9572                        long msdelay)\r
9573 {\r
9574   /* Ignore delay, not implemented for WinBoard */\r
9575   return OutputToProcess(pr, message, count, outError);\r
9576 }\r
9577 \r
9578 \r
9579 void\r
9580 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9581                         char *buf, int count, int error)\r
9582 {\r
9583   DisplayFatalError(_("Not implemented"), 0, 1);\r
9584 }\r
9585 \r
9586 /* see wgamelist.c for Game List functions */\r
9587 /* see wedittags.c for Edit Tags functions */\r
9588 \r
9589 \r
9590 VOID\r
9591 ICSInitScript()\r
9592 {\r
9593   FILE *f;\r
9594   char buf[MSG_SIZ];\r
9595   char *dummy;\r
9596 \r
9597   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9598     f = fopen(buf, "r");\r
9599     if (f != NULL) {\r
9600       ProcessICSInitScript(f);\r
9601       fclose(f);\r
9602     }\r
9603   }\r
9604 }\r
9605 \r
9606 \r
9607 VOID\r
9608 StartAnalysisClock()\r
9609 {\r
9610   if (analysisTimerEvent) return;\r
9611   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9612                                         (UINT) 2000, NULL);\r
9613 }\r
9614 \r
9615 VOID\r
9616 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9617 {\r
9618   highlightInfo.sq[0].x = fromX;\r
9619   highlightInfo.sq[0].y = fromY;\r
9620   highlightInfo.sq[1].x = toX;\r
9621   highlightInfo.sq[1].y = toY;\r
9622 }\r
9623 \r
9624 VOID\r
9625 ClearHighlights()\r
9626 {\r
9627   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9628     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9629 }\r
9630 \r
9631 VOID\r
9632 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9633 {\r
9634   premoveHighlightInfo.sq[0].x = fromX;\r
9635   premoveHighlightInfo.sq[0].y = fromY;\r
9636   premoveHighlightInfo.sq[1].x = toX;\r
9637   premoveHighlightInfo.sq[1].y = toY;\r
9638 }\r
9639 \r
9640 VOID\r
9641 ClearPremoveHighlights()\r
9642 {\r
9643   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9644     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9645 }\r
9646 \r
9647 VOID\r
9648 ShutDownFrontEnd()\r
9649 {\r
9650   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9651   DeleteClipboardTempFiles();\r
9652 }\r
9653 \r
9654 void\r
9655 BoardToTop()\r
9656 {\r
9657     if (IsIconic(hwndMain))\r
9658       ShowWindow(hwndMain, SW_RESTORE);\r
9659 \r
9660     SetActiveWindow(hwndMain);\r
9661 }\r
9662 \r
9663 /*\r
9664  * Prototypes for animation support routines\r
9665  */\r
9666 static void ScreenSquare(int column, int row, POINT * pt);\r
9667 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9668      POINT frames[], int * nFrames);\r
9669 \r
9670 \r
9671 #define kFactor 4\r
9672 \r
9673 void\r
9674 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9675 {       // [HGM] atomic: animate blast wave\r
9676         int i;\r
9677 \r
9678         explodeInfo.fromX = fromX;\r
9679         explodeInfo.fromY = fromY;\r
9680         explodeInfo.toX = toX;\r
9681         explodeInfo.toY = toY;\r
9682         for(i=1; i<4*kFactor; i++) {\r
9683             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9684             DrawPosition(FALSE, board);\r
9685             Sleep(appData.animSpeed);\r
9686         }\r
9687         explodeInfo.radius = 0;\r
9688         DrawPosition(TRUE, board);\r
9689 }\r
9690 \r
9691 void\r
9692 AnimateMove(board, fromX, fromY, toX, toY)\r
9693      Board board;\r
9694      int fromX;\r
9695      int fromY;\r
9696      int toX;\r
9697      int toY;\r
9698 {\r
9699   ChessSquare piece;\r
9700   POINT start, finish, mid;\r
9701   POINT frames[kFactor * 2 + 1];\r
9702   int nFrames, n;\r
9703 \r
9704   if (!appData.animate) return;\r
9705   if (doingSizing) return;\r
9706   if (fromY < 0 || fromX < 0) return;\r
9707   piece = board[fromY][fromX];\r
9708   if (piece >= EmptySquare) return;\r
9709 \r
9710   ScreenSquare(fromX, fromY, &start);\r
9711   ScreenSquare(toX, toY, &finish);\r
9712 \r
9713   /* All moves except knight jumps move in straight line */\r
9714   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9715     mid.x = start.x + (finish.x - start.x) / 2;\r
9716     mid.y = start.y + (finish.y - start.y) / 2;\r
9717   } else {\r
9718     /* Knight: make straight movement then diagonal */\r
9719     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9720        mid.x = start.x + (finish.x - start.x) / 2;\r
9721        mid.y = start.y;\r
9722      } else {\r
9723        mid.x = start.x;\r
9724        mid.y = start.y + (finish.y - start.y) / 2;\r
9725      }\r
9726   }\r
9727   \r
9728   /* Don't use as many frames for very short moves */\r
9729   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9730     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9731   else\r
9732     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9733 \r
9734   animInfo.from.x = fromX;\r
9735   animInfo.from.y = fromY;\r
9736   animInfo.to.x = toX;\r
9737   animInfo.to.y = toY;\r
9738   animInfo.lastpos = start;\r
9739   animInfo.piece = piece;\r
9740   for (n = 0; n < nFrames; n++) {\r
9741     animInfo.pos = frames[n];\r
9742     DrawPosition(FALSE, NULL);\r
9743     animInfo.lastpos = animInfo.pos;\r
9744     Sleep(appData.animSpeed);\r
9745   }\r
9746   animInfo.pos = finish;\r
9747   DrawPosition(FALSE, NULL);\r
9748   animInfo.piece = EmptySquare;\r
9749   Explode(board, fromX, fromY, toX, toY);\r
9750 }\r
9751 \r
9752 /*      Convert board position to corner of screen rect and color       */\r
9753 \r
9754 static void\r
9755 ScreenSquare(column, row, pt)\r
9756      int column; int row; POINT * pt;\r
9757 {\r
9758   if (flipView) {\r
9759     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9760     pt->y = lineGap + row * (squareSize + lineGap);\r
9761   } else {\r
9762     pt->x = lineGap + column * (squareSize + lineGap);\r
9763     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9764   }\r
9765 }\r
9766 \r
9767 /*      Generate a series of frame coords from start->mid->finish.\r
9768         The movement rate doubles until the half way point is\r
9769         reached, then halves back down to the final destination,\r
9770         which gives a nice slow in/out effect. The algorithmn\r
9771         may seem to generate too many intermediates for short\r
9772         moves, but remember that the purpose is to attract the\r
9773         viewers attention to the piece about to be moved and\r
9774         then to where it ends up. Too few frames would be less\r
9775         noticeable.                                             */\r
9776 \r
9777 static void\r
9778 Tween(start, mid, finish, factor, frames, nFrames)\r
9779      POINT * start; POINT * mid;\r
9780      POINT * finish; int factor;\r
9781      POINT frames[]; int * nFrames;\r
9782 {\r
9783   int n, fraction = 1, count = 0;\r
9784 \r
9785   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9786   for (n = 0; n < factor; n++)\r
9787     fraction *= 2;\r
9788   for (n = 0; n < factor; n++) {\r
9789     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9790     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9791     count ++;\r
9792     fraction = fraction / 2;\r
9793   }\r
9794   \r
9795   /* Midpoint */\r
9796   frames[count] = *mid;\r
9797   count ++;\r
9798   \r
9799   /* Slow out, stepping 1/2, then 1/4, ... */\r
9800   fraction = 2;\r
9801   for (n = 0; n < factor; n++) {\r
9802     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9803     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9804     count ++;\r
9805     fraction = fraction * 2;\r
9806   }\r
9807   *nFrames = count;\r
9808 }\r
9809 \r
9810 void\r
9811 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9812 {\r
9813     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9814 \r
9815     EvalGraphSet( first, last, current, pvInfoList );\r
9816 }\r
9817 \r
9818 void\r
9819 SettingsPopUp(ChessProgramState *cps)\r
9820 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9821       EngineOptionsPopup(savedHwnd, cps);\r
9822 }\r
9823 \r
9824 int flock(int fid, int code)\r
9825 {\r
9826     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9827     OVERLAPPED ov;\r
9828     ov.hEvent = NULL;\r
9829     ov.Offset = 0;\r
9830     ov.OffsetHigh = 0;\r
9831     switch(code) {\r
9832       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9833       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9834       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9835       default: return -1;\r
9836     }\r
9837     return 0;\r
9838 }\r