0ff5e0b8063c80d0d809ba48f9ded90a6f92c899
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern int ics_type;\r
104 \r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
106 VOID NewVariantPopup(HWND hwnd);\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
108                    /*char*/int promoChar));\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127   ChessSquare piece;\r
128 } DragInfo;\r
129 \r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
131 \r
132 typedef struct {\r
133   POINT sq[2];    /* board coordinates of from, to squares */\r
134 } HighlightInfo;\r
135 \r
136 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
140 \r
141 typedef struct { // [HGM] atomic\r
142   int fromX, fromY, toX, toY, radius;\r
143 } ExplodeInfo;\r
144 \r
145 static ExplodeInfo explodeInfo;\r
146 \r
147 /* Window class names */\r
148 char szAppName[] = "WinBoard";\r
149 char szConsoleName[] = "WBConsole";\r
150 \r
151 /* Title bar text */\r
152 char szTitle[] = "WinBoard";\r
153 char szConsoleTitle[] = "I C S Interaction";\r
154 \r
155 char *programName;\r
156 char *settingsFileName;\r
157 Boolean saveSettingsOnExit;\r
158 char installDir[MSG_SIZ];\r
159 char homeDir[MSG_SIZ];\r
160 int errorExitStatus;\r
161 \r
162 BoardSize boardSize;\r
163 Boolean chessProgram;\r
164 //static int boardX, boardY;\r
165 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
166 int squareSize, lineGap, minorSize;\r
167 static int winW, winH;\r
168 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
169 static int logoHeight = 0;\r
170 static char messageText[MESSAGE_TEXT_MAX];\r
171 static int clockTimerEvent = 0;\r
172 static int loadGameTimerEvent = 0;\r
173 static int analysisTimerEvent = 0;\r
174 static DelayedEventCallback delayedTimerCallback;\r
175 static int delayedTimerEvent = 0;\r
176 static int buttonCount = 2;\r
177 char *icsTextMenuString;\r
178 char *icsNames;\r
179 char *firstChessProgramNames;\r
180 char *secondChessProgramNames;\r
181 \r
182 #define PALETTESIZE 256\r
183 \r
184 HINSTANCE hInst;          /* current instance */\r
185 Boolean alwaysOnTop = FALSE;\r
186 RECT boardRect;\r
187 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
188   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
189 HPALETTE hPal;\r
190 ColorClass currentColorClass;\r
191 \r
192 static HWND savedHwnd;\r
193 HWND hCommPort = NULL;    /* currently open comm port */\r
194 static HWND hwndPause;    /* pause button */\r
195 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
196 static HBRUSH lightSquareBrush, darkSquareBrush,\r
197   blackSquareBrush, /* [HGM] for band between board and holdings */\r
198   explodeBrush,     /* [HGM] atomic */\r
199   markerBrush,      /* [HGM] markers */\r
200   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
201 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
202 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
203 static HPEN gridPen = NULL;\r
204 static HPEN highlightPen = NULL;\r
205 static HPEN premovePen = NULL;\r
206 static NPLOGPALETTE pLogPal;\r
207 static BOOL paletteChanged = FALSE;\r
208 static HICON iconWhite, iconBlack, iconCurrent;\r
209 static int doingSizing = FALSE;\r
210 static int lastSizing = 0;\r
211 static int prevStderrPort;\r
212 static HBITMAP userLogo;\r
213 \r
214 static HBITMAP liteBackTexture = NULL;\r
215 static HBITMAP darkBackTexture = NULL;\r
216 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int backTextureSquareSize = 0;\r
219 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
220 \r
221 #if __GNUC__ && !defined(_winmajor)\r
222 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
223 #else\r
224 #if defined(_winmajor)\r
225 #define oldDialog (_winmajor < 4)\r
226 #else\r
227 #define oldDialog 0\r
228 #endif\r
229 #endif\r
230 \r
231 #define INTERNATIONAL\r
232 \r
233 #ifdef INTERNATIONAL\r
234 #  define _(s) T_(s)\r
235 #  define N_(s) s\r
236 #else\r
237 #  define _(s) s\r
238 #  define N_(s) s\r
239 #  define T_(s) s\r
240 #  define Translate(x, y)\r
241 #  define LoadLanguageFile(s)\r
242 #endif\r
243 \r
244 #ifdef INTERNATIONAL\r
245 \r
246 Boolean barbaric; // flag indicating if translation is needed\r
247 \r
248 // list of item numbers used in each dialog (used to alter language at run time)\r
249 \r
250 #define ABOUTBOX -1  /* not sure why these are needed */\r
251 #define ABOUTBOX2 -1\r
252 \r
253 int dialogItems[][40] = {\r
254 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
255 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
256   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
257 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
259   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
262   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
265   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
266 { ABOUTBOX2, IDC_ChessBoard }, \r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
268   OPT_GameListClose, IDC_GameListDoFilter }, \r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
270 { DLG_Error, IDOK }, \r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
272   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
275   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
276   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
277 { DLG_IndexNumber, IDC_Index }, \r
278 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
279 { DLG_TypeInName, IDOK, IDCANCEL }, \r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
281   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
283   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
284   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
285   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
286   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
287   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
288   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
290   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
291   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
292   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
293   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
294   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
295   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
296   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
297   GPB_General, GPB_Alarm }, \r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
299   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
300   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
301   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
302   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
303   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
304   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
305   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
307   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
308   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
309   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
310   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
311   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
312   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
313   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
314   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
316   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
317   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
318   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
319 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
320 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
321   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
322 { DLG_MoveHistory }, \r
323 { DLG_EvalGraph }, \r
324 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
325 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
326 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
327   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
328   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
329   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
330 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
331   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
332   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
333 { 0 }\r
334 };\r
335 \r
336 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
337 static int lastChecked;\r
338 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
339 extern int tinyLayout;\r
340 extern char * menuBarText[][10];\r
341 \r
342 void\r
343 LoadLanguageFile(char *name)\r
344 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
345     FILE *f;\r
346     int i=0, j=0, n=0, k;\r
347     char buf[MSG_SIZ];\r
348 \r
349     if(!name || name[0] == NULLCHAR) return;\r
350       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
351     appData.language = oldLanguage;\r
352     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
353     if((f = fopen(buf, "r")) == NULL) return;\r
354     while((k = fgetc(f)) != EOF) {\r
355         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
356         languageBuf[i] = k;\r
357         if(k == '\n') {\r
358             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
359                 char *p;\r
360                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
361                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
362                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
363                         english[j] = languageBuf + n + 1; *p = 0;\r
364                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
365 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
366                     }\r
367                 }\r
368             }\r
369             n = i + 1;\r
370         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
371             switch(k) {\r
372               case 'n': k = '\n'; break;\r
373               case 'r': k = '\r'; break;\r
374               case 't': k = '\t'; break;\r
375             }\r
376             languageBuf[--i] = k;\r
377         }\r
378         i++;\r
379     }\r
380     fclose(f);\r
381     barbaric = (j != 0);\r
382     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
383 }\r
384 \r
385 char *\r
386 T_(char *s)\r
387 {   // return the translation of the given string\r
388     // efficiency can be improved a lot...\r
389     int i=0;\r
390 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
391     if(!barbaric) return s;\r
392     if(!s) return ""; // sanity\r
393     while(english[i]) {\r
394         if(!strcmp(s, english[i])) return foreign[i];\r
395         i++;\r
396     }\r
397     return s;\r
398 }\r
399 \r
400 void\r
401 Translate(HWND hDlg, int dialogID)\r
402 {   // translate all text items in the given dialog\r
403     int i=0, j, k;\r
404     char buf[MSG_SIZ], *s;\r
405     if(!barbaric) return;\r
406     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
407     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
408     GetWindowText( hDlg, buf, MSG_SIZ );\r
409     s = T_(buf);\r
410     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
411     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
412         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
413         if(strlen(buf) == 0) continue;\r
414         s = T_(buf);\r
415         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
416     }\r
417 }\r
418 \r
419 HMENU\r
420 TranslateOneMenu(int i, HMENU subMenu)\r
421 {\r
422     int j;\r
423     static MENUITEMINFO info;\r
424 \r
425     info.cbSize = sizeof(MENUITEMINFO);\r
426     info.fMask = MIIM_STATE | MIIM_TYPE;\r
427           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
428             char buf[MSG_SIZ];\r
429             info.dwTypeData = buf;\r
430             info.cch = sizeof(buf);\r
431             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
432             if(i < 10) {
433                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
434                 else menuText[i][j] = strdup(buf); // remember original on first change\r
435             }\r
436             if(buf[0] == NULLCHAR) continue;\r
437             info.dwTypeData = T_(buf);\r
438             info.cch = strlen(buf)+1;\r
439             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
440           }\r
441     return subMenu;\r
442 }\r
443 \r
444 void\r
445 TranslateMenus(int addLanguage)\r
446 {\r
447     int i;\r
448     WIN32_FIND_DATA fileData;\r
449     HANDLE hFind;\r
450 #define IDM_English 1970\r
451     if(1) {\r
452         HMENU mainMenu = GetMenu(hwndMain);\r
453         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
454           HMENU subMenu = GetSubMenu(mainMenu, i);\r
455           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
456                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
457           TranslateOneMenu(i, subMenu);\r
458         }\r
459         DrawMenuBar(hwndMain);\r
460     }\r
461 \r
462     if(!addLanguage) return;\r
463     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
464         HMENU mainMenu = GetMenu(hwndMain);\r
465         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
466         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
467         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
468         i = 0; lastChecked = IDM_English;\r
469         do {\r
470             char *p, *q = fileData.cFileName;\r
471             int checkFlag = MF_UNCHECKED;\r
472             languageFile[i] = strdup(q);\r
473             if(barbaric && !strcmp(oldLanguage, q)) {\r
474                 checkFlag = MF_CHECKED;\r
475                 lastChecked = IDM_English + i + 1;\r
476                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
477             }\r
478             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
479             p = strstr(fileData.cFileName, ".lng");\r
480             if(p) *p = 0;\r
481             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
482         } while(FindNextFile(hFind, &fileData));\r
483         FindClose(hFind);\r
484     }\r
485 }\r
486 \r
487 #endif\r
488 \r
489 typedef struct {\r
490   char *name;\r
491   int squareSize;\r
492   int lineGap;\r
493   int smallLayout;\r
494   int tinyLayout;\r
495   int cliWidth, cliHeight;\r
496 } SizeInfo;\r
497 \r
498 SizeInfo sizeInfo[] = \r
499 {\r
500   { "tiny",     21, 0, 1, 1, 0, 0 },\r
501   { "teeny",    25, 1, 1, 1, 0, 0 },\r
502   { "dinky",    29, 1, 1, 1, 0, 0 },\r
503   { "petite",   33, 1, 1, 1, 0, 0 },\r
504   { "slim",     37, 2, 1, 0, 0, 0 },\r
505   { "small",    40, 2, 1, 0, 0, 0 },\r
506   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
507   { "middling", 49, 2, 0, 0, 0, 0 },\r
508   { "average",  54, 2, 0, 0, 0, 0 },\r
509   { "moderate", 58, 3, 0, 0, 0, 0 },\r
510   { "medium",   64, 3, 0, 0, 0, 0 },\r
511   { "bulky",    72, 3, 0, 0, 0, 0 },\r
512   { "large",    80, 3, 0, 0, 0, 0 },\r
513   { "big",      87, 3, 0, 0, 0, 0 },\r
514   { "huge",     95, 3, 0, 0, 0, 0 },\r
515   { "giant",    108, 3, 0, 0, 0, 0 },\r
516   { "colossal", 116, 4, 0, 0, 0, 0 },\r
517   { "titanic",  129, 4, 0, 0, 0, 0 },\r
518   { NULL, 0, 0, 0, 0, 0, 0 }\r
519 };\r
520 \r
521 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
522 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
523 {\r
524   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
525   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
526   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
527   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
528   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
529   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
530   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
531   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
532   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
533   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
534   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
535   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
536   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
537   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
538   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
539   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
540   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
541   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
542 };\r
543 \r
544 MyFont *font[NUM_SIZES][NUM_FONTS];\r
545 \r
546 typedef struct {\r
547   char *label;\r
548   int id;\r
549   HWND hwnd;\r
550   WNDPROC wndproc;\r
551 } MyButtonDesc;\r
552 \r
553 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
554 #define N_BUTTONS 5\r
555 \r
556 MyButtonDesc buttonDesc[N_BUTTONS] =\r
557 {\r
558   {"<<", IDM_ToStart, NULL, NULL},\r
559   {"<", IDM_Backward, NULL, NULL},\r
560   {"P", IDM_Pause, NULL, NULL},\r
561   {">", IDM_Forward, NULL, NULL},\r
562   {">>", IDM_ToEnd, NULL, NULL},\r
563 };\r
564 \r
565 int tinyLayout = 0, smallLayout = 0;\r
566 #define MENU_BAR_ITEMS 9\r
567 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
568   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
569   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
570 };\r
571 \r
572 \r
573 MySound sounds[(int)NSoundClasses];\r
574 MyTextAttribs textAttribs[(int)NColorClasses];\r
575 \r
576 MyColorizeAttribs colorizeAttribs[] = {\r
577   { (COLORREF)0, 0, N_("Shout Text") },\r
578   { (COLORREF)0, 0, N_("SShout/CShout") },\r
579   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
580   { (COLORREF)0, 0, N_("Channel Text") },\r
581   { (COLORREF)0, 0, N_("Kibitz Text") },\r
582   { (COLORREF)0, 0, N_("Tell Text") },\r
583   { (COLORREF)0, 0, N_("Challenge Text") },\r
584   { (COLORREF)0, 0, N_("Request Text") },\r
585   { (COLORREF)0, 0, N_("Seek Text") },\r
586   { (COLORREF)0, 0, N_("Normal Text") },\r
587   { (COLORREF)0, 0, N_("None") }\r
588 };\r
589 \r
590 \r
591 \r
592 static char *commentTitle;\r
593 static char *commentText;\r
594 static int commentIndex;\r
595 static Boolean editComment = FALSE;\r
596 \r
597 \r
598 char errorTitle[MSG_SIZ];\r
599 char errorMessage[2*MSG_SIZ];\r
600 HWND errorDialog = NULL;\r
601 BOOLEAN moveErrorMessageUp = FALSE;\r
602 BOOLEAN consoleEcho = TRUE;\r
603 CHARFORMAT consoleCF;\r
604 COLORREF consoleBackgroundColor;\r
605 \r
606 char *programVersion;\r
607 \r
608 #define CPReal 1\r
609 #define CPComm 2\r
610 #define CPSock 3\r
611 #define CPRcmd 4\r
612 typedef int CPKind;\r
613 \r
614 typedef struct {\r
615   CPKind kind;\r
616   HANDLE hProcess;\r
617   DWORD pid;\r
618   HANDLE hTo;\r
619   HANDLE hFrom;\r
620   SOCKET sock;\r
621   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
622 } ChildProc;\r
623 \r
624 #define INPUT_SOURCE_BUF_SIZE 4096\r
625 \r
626 typedef struct _InputSource {\r
627   CPKind kind;\r
628   HANDLE hFile;\r
629   SOCKET sock;\r
630   int lineByLine;\r
631   HANDLE hThread;\r
632   DWORD id;\r
633   char buf[INPUT_SOURCE_BUF_SIZE];\r
634   char *next;\r
635   DWORD count;\r
636   int error;\r
637   InputCallback func;\r
638   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
639   VOIDSTAR closure;\r
640 } InputSource;\r
641 \r
642 InputSource *consoleInputSource;\r
643 \r
644 DCB dcb;\r
645 \r
646 /* forward */\r
647 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
648 VOID ConsoleCreate();\r
649 LRESULT CALLBACK\r
650   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
651 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
652 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
653 VOID ParseCommSettings(char *arg, DCB *dcb);\r
654 LRESULT CALLBACK\r
655   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
656 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
657 void ParseIcsTextMenu(char *icsTextMenuString);\r
658 VOID PopUpNameDialog(char firstchar);\r
659 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
660 \r
661 /* [AS] */\r
662 int NewGameFRC();\r
663 int GameListOptions();\r
664 \r
665 int dummy; // [HGM] for obsolete args\r
666 \r
667 HWND hwndMain = NULL;        /* root window*/\r
668 HWND hwndConsole = NULL;\r
669 HWND commentDialog = NULL;\r
670 HWND moveHistoryDialog = NULL;\r
671 HWND evalGraphDialog = NULL;\r
672 HWND engineOutputDialog = NULL;\r
673 HWND gameListDialog = NULL;\r
674 HWND editTagsDialog = NULL;\r
675 \r
676 int commentUp = FALSE;\r
677 \r
678 WindowPlacement wpMain;\r
679 WindowPlacement wpConsole;\r
680 WindowPlacement wpComment;\r
681 WindowPlacement wpMoveHistory;\r
682 WindowPlacement wpEvalGraph;\r
683 WindowPlacement wpEngineOutput;\r
684 WindowPlacement wpGameList;\r
685 WindowPlacement wpTags;\r
686 \r
687 VOID EngineOptionsPopup(); // [HGM] settings\r
688 \r
689 VOID GothicPopUp(char *title, VariantClass variant);\r
690 /*\r
691  * Setting "frozen" should disable all user input other than deleting\r
692  * the window.  We do this while engines are initializing themselves.\r
693  */\r
694 static int frozen = 0;\r
695 static int oldMenuItemState[MENU_BAR_ITEMS];\r
696 void FreezeUI()\r
697 {\r
698   HMENU hmenu;\r
699   int i;\r
700 \r
701   if (frozen) return;\r
702   frozen = 1;\r
703   hmenu = GetMenu(hwndMain);\r
704   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
705     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
706   }\r
707   DrawMenuBar(hwndMain);\r
708 }\r
709 \r
710 /* Undo a FreezeUI */\r
711 void ThawUI()\r
712 {\r
713   HMENU hmenu;\r
714   int i;\r
715 \r
716   if (!frozen) return;\r
717   frozen = 0;\r
718   hmenu = GetMenu(hwndMain);\r
719   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
720     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
721   }\r
722   DrawMenuBar(hwndMain);\r
723 }\r
724 \r
725 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
726 \r
727 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
728 #ifdef JAWS\r
729 #include "jaws.c"\r
730 #else\r
731 #define JAWS_INIT\r
732 #define JAWS_ARGS\r
733 #define JAWS_ALT_INTERCEPT\r
734 #define JAWS_KB_NAVIGATION\r
735 #define JAWS_MENU_ITEMS\r
736 #define JAWS_SILENCE\r
737 #define JAWS_REPLAY\r
738 #define JAWS_ACCEL\r
739 #define JAWS_COPYRIGHT\r
740 #define JAWS_DELETE(X) X\r
741 #define SAYMACHINEMOVE()\r
742 #define SAY(X)\r
743 #endif\r
744 \r
745 /*---------------------------------------------------------------------------*\\r
746  *\r
747  * WinMain\r
748  *\r
749 \*---------------------------------------------------------------------------*/\r
750 \r
751 int APIENTRY\r
752 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
753         LPSTR lpCmdLine, int nCmdShow)\r
754 {\r
755   MSG msg;\r
756   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
757 //  INITCOMMONCONTROLSEX ex;\r
758 \r
759   debugFP = stderr;\r
760 \r
761   LoadLibrary("RICHED32.DLL");\r
762   consoleCF.cbSize = sizeof(CHARFORMAT);\r
763 \r
764   if (!InitApplication(hInstance)) {\r
765     return (FALSE);\r
766   }\r
767   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
768     return (FALSE);\r
769   }\r
770 \r
771   JAWS_INIT\r
772   TranslateMenus(1);\r
773 \r
774 //  InitCommonControlsEx(&ex);\r
775   InitCommonControls();\r
776 \r
777   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
778   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
779   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
780 \r
781   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
782 \r
783   while (GetMessage(&msg, /* message structure */\r
784                     NULL, /* handle of window receiving the message */\r
785                     0,    /* lowest message to examine */\r
786                     0))   /* highest message to examine */\r
787     {\r
788 \r
789       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
790         // [HGM] navigate: switch between all windows with tab\r
791         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
792         int i, currentElement = 0;\r
793 \r
794         // first determine what element of the chain we come from (if any)\r
795         if(appData.icsActive) {\r
796             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
797             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
798         }\r
799         if(engineOutputDialog && EngineOutputIsUp()) {\r
800             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
801             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
802         }\r
803         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
804             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
805         }\r
806         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
807         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
808         if(msg.hwnd == e1)                 currentElement = 2; else\r
809         if(msg.hwnd == e2)                 currentElement = 3; else\r
810         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
811         if(msg.hwnd == mh)                currentElement = 4; else\r
812         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
813         if(msg.hwnd == hText)  currentElement = 5; else\r
814         if(msg.hwnd == hInput) currentElement = 6; else\r
815         for (i = 0; i < N_BUTTONS; i++) {\r
816             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
817         }\r
818 \r
819         // determine where to go to\r
820         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
821           do {\r
822             currentElement = (currentElement + direction) % 7;\r
823             switch(currentElement) {\r
824                 case 0:\r
825                   h = hwndMain; break; // passing this case always makes the loop exit\r
826                 case 1:\r
827                   h = buttonDesc[0].hwnd; break; // could be NULL\r
828                 case 2:\r
829                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
830                   h = e1; break;\r
831                 case 3:\r
832                   if(!EngineOutputIsUp()) continue;\r
833                   h = e2; break;\r
834                 case 4:\r
835                   if(!MoveHistoryIsUp()) continue;\r
836                   h = mh; break;\r
837 //              case 6: // input to eval graph does not seem to get here!\r
838 //                if(!EvalGraphIsUp()) continue;\r
839 //                h = evalGraphDialog; break;\r
840                 case 5:\r
841                   if(!appData.icsActive) continue;\r
842                   SAY("display");\r
843                   h = hText; break;\r
844                 case 6:\r
845                   if(!appData.icsActive) continue;\r
846                   SAY("input");\r
847                   h = hInput; break;\r
848             }\r
849           } while(h == 0);\r
850 \r
851           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
852           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
853           SetFocus(h);\r
854 \r
855           continue; // this message now has been processed\r
856         }\r
857       }\r
858 \r
859       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
860           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
861           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
862           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
863           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
864           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
865           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
866           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
867           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
868           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
869         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
870         for(i=0; i<MAX_CHAT; i++) \r
871             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
872                 done = 1; break;\r
873         }\r
874         if(done) continue; // [HGM] chat: end patch\r
875         TranslateMessage(&msg); /* Translates virtual key codes */\r
876         DispatchMessage(&msg);  /* Dispatches message to window */\r
877       }\r
878     }\r
879 \r
880 \r
881   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
882 }\r
883 \r
884 /*---------------------------------------------------------------------------*\\r
885  *\r
886  * Initialization functions\r
887  *\r
888 \*---------------------------------------------------------------------------*/\r
889 \r
890 void\r
891 SetUserLogo()\r
892 {   // update user logo if necessary\r
893     static char oldUserName[MSG_SIZ], *curName;\r
894 \r
895     if(appData.autoLogo) {\r
896           curName = UserName();\r
897           if(strcmp(curName, oldUserName)) {\r
898             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
899                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
900                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
901                 if(userLogo == NULL)\r
902                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
903           }\r
904     }\r
905 }\r
906 \r
907 BOOL\r
908 InitApplication(HINSTANCE hInstance)\r
909 {\r
910   WNDCLASS wc;\r
911 \r
912   /* Fill in window class structure with parameters that describe the */\r
913   /* main window. */\r
914 \r
915   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
916   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
917   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
918   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
919   wc.hInstance     = hInstance;         /* Owner of this class */\r
920   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
921   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
922   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
923   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
924   wc.lpszClassName = szAppName;                 /* Name to register as */\r
925 \r
926   /* Register the window class and return success/failure code. */\r
927   if (!RegisterClass(&wc)) return FALSE;\r
928 \r
929   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
930   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
931   wc.cbClsExtra    = 0;\r
932   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
933   wc.hInstance     = hInstance;\r
934   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
935   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
936   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
937   wc.lpszMenuName  = NULL;\r
938   wc.lpszClassName = szConsoleName;\r
939 \r
940   if (!RegisterClass(&wc)) return FALSE;\r
941   return TRUE;\r
942 }\r
943 \r
944 \r
945 /* Set by InitInstance, used by EnsureOnScreen */\r
946 int screenHeight, screenWidth;\r
947 \r
948 void\r
949 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
950 {\r
951 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
952   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
953   if (*x > screenWidth - 32) *x = 0;\r
954   if (*y > screenHeight - 32) *y = 0;\r
955   if (*x < minX) *x = minX;\r
956   if (*y < minY) *y = minY;\r
957 }\r
958 \r
959 VOID\r
960 LoadLogo(ChessProgramState *cps, int n)\r
961 {\r
962   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
963       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
964 \r
965       if (cps->programLogo == NULL && appData.debugMode) {\r
966           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
967       }\r
968   } else if(appData.autoLogo) {\r
969       if(appData.firstDirectory && appData.directory[n][0]) {\r
970         char buf[MSG_SIZ];\r
971           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.directory[n]);\r
972         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
973       }\r
974   }\r
975 }\r
976 \r
977 BOOL\r
978 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
979 {\r
980   HWND hwnd; /* Main window handle. */\r
981   int ibs;\r
982   WINDOWPLACEMENT wp;\r
983   char *filepart;\r
984 \r
985   hInst = hInstance;    /* Store instance handle in our global variable */\r
986   programName = szAppName;\r
987 \r
988   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
989     *filepart = NULLCHAR;\r
990   } else {\r
991     GetCurrentDirectory(MSG_SIZ, installDir);\r
992   }\r
993   safeStrCpy(homeDir, installDir, MSG_SIZ);\r
994   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
995   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
996   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
997   /* xboard, and older WinBoards, controlled the move sound with the\r
998      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
999      always turn the option on (so that the backend will call us),\r
1000      then let the user turn the sound off by setting it to silence if\r
1001      desired.  To accommodate old winboard.ini files saved by old\r
1002      versions of WinBoard, we also turn off the sound if the option\r
1003      was initially set to false. [HGM] taken out of InitAppData */\r
1004   if (!appData.ringBellAfterMoves) {\r
1005     sounds[(int)SoundMove].name = strdup("");\r
1006     appData.ringBellAfterMoves = TRUE;\r
1007   }\r
1008   if (appData.debugMode) {\r
1009     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1010     setbuf(debugFP, NULL);\r
1011   }\r
1012 \r
1013   LoadLanguageFile(appData.language);\r
1014 \r
1015   InitBackEnd1();\r
1016 \r
1017 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1018 //  InitEngineUCI( installDir, &second );\r
1019 \r
1020   /* Create a main window for this application instance. */\r
1021   hwnd = CreateWindow(szAppName, szTitle,\r
1022                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1023                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1024                       NULL, NULL, hInstance, NULL);\r
1025   hwndMain = hwnd;\r
1026 \r
1027   /* If window could not be created, return "failure" */\r
1028   if (!hwnd) {\r
1029     return (FALSE);\r
1030   }\r
1031 \r
1032   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1033   LoadLogo(&first, 0);\r
1034 \r
1035   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1036       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1037 \r
1038       if (second.programLogo == NULL && appData.debugMode) {\r
1039           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1040       }\r
1041   } else if(appData.autoLogo) {\r
1042       char buf[MSG_SIZ];\r
1043       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1044         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1045         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1046       } else\r
1047       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1048         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1049         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1050       }\r
1051   }\r
1052 \r
1053   SetUserLogo();\r
1054 \r
1055   iconWhite = LoadIcon(hInstance, "icon_white");\r
1056   iconBlack = LoadIcon(hInstance, "icon_black");\r
1057   iconCurrent = iconWhite;\r
1058   InitDrawingColors();\r
1059   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1060   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1061   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1062     /* Compute window size for each board size, and use the largest\r
1063        size that fits on this screen as the default. */\r
1064     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1065     if (boardSize == (BoardSize)-1 &&\r
1066         winH <= screenHeight\r
1067            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1068         && winW <= screenWidth) {\r
1069       boardSize = (BoardSize)ibs;\r
1070     }\r
1071   }\r
1072 \r
1073   InitDrawingSizes(boardSize, 0);\r
1074   InitMenuChecks();\r
1075   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1076 \r
1077   /* [AS] Load textures if specified */\r
1078   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1079   \r
1080   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1081       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1082       liteBackTextureMode = appData.liteBackTextureMode;\r
1083 \r
1084       if (liteBackTexture == NULL && appData.debugMode) {\r
1085           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1086       }\r
1087   }\r
1088   \r
1089   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1090       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1091       darkBackTextureMode = appData.darkBackTextureMode;\r
1092 \r
1093       if (darkBackTexture == NULL && appData.debugMode) {\r
1094           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1095       }\r
1096   }\r
1097 \r
1098   mysrandom( (unsigned) time(NULL) );\r
1099 \r
1100   /* [AS] Restore layout */\r
1101   if( wpMoveHistory.visible ) {\r
1102       MoveHistoryPopUp();\r
1103   }\r
1104 \r
1105   if( wpEvalGraph.visible ) {\r
1106       EvalGraphPopUp();\r
1107   }\r
1108 \r
1109   if( wpEngineOutput.visible ) {\r
1110       EngineOutputPopUp();\r
1111   }\r
1112 \r
1113   /* Make the window visible; update its client area; and return "success" */\r
1114   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1115   wp.length = sizeof(WINDOWPLACEMENT);\r
1116   wp.flags = 0;\r
1117   wp.showCmd = nCmdShow;\r
1118   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1119   wp.rcNormalPosition.left = wpMain.x;\r
1120   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1121   wp.rcNormalPosition.top = wpMain.y;\r
1122   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1123   SetWindowPlacement(hwndMain, &wp);\r
1124 \r
1125   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1126 \r
1127   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1128                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1129 \r
1130   if (hwndConsole) {\r
1131 #if AOT_CONSOLE\r
1132     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1133                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1134 #endif\r
1135     ShowWindow(hwndConsole, nCmdShow);\r
1136     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1137       char buf[MSG_SIZ], *p = buf, *q;\r
1138         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1139       do {\r
1140         q = strchr(p, ';');\r
1141         if(q) *q++ = 0;\r
1142         if(*p) ChatPopUp(p);\r
1143       } while(p=q);\r
1144     }\r
1145     SetActiveWindow(hwndConsole);\r
1146   }\r
1147   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1148   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1149 \r
1150   return TRUE;\r
1151 \r
1152 }\r
1153 \r
1154 VOID\r
1155 InitMenuChecks()\r
1156 {\r
1157   HMENU hmenu = GetMenu(hwndMain);\r
1158 \r
1159   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1160                         MF_BYCOMMAND|((appData.icsActive &&\r
1161                                        *appData.icsCommPort != NULLCHAR) ?\r
1162                                       MF_ENABLED : MF_GRAYED));\r
1163   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1164                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1165                                      MF_CHECKED : MF_UNCHECKED));\r
1166 }\r
1167 \r
1168 //---------------------------------------------------------------------------------------------------------\r
1169 \r
1170 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1171 #define XBOARD FALSE\r
1172 \r
1173 #define OPTCHAR "/"\r
1174 #define SEPCHAR "="\r
1175 \r
1176 #include "args.h"\r
1177 \r
1178 // front-end part of option handling\r
1179 \r
1180 VOID\r
1181 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1182 {\r
1183   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1184   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1185   DeleteDC(hdc);\r
1186   lf->lfWidth = 0;\r
1187   lf->lfEscapement = 0;\r
1188   lf->lfOrientation = 0;\r
1189   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1190   lf->lfItalic = mfp->italic;\r
1191   lf->lfUnderline = mfp->underline;\r
1192   lf->lfStrikeOut = mfp->strikeout;\r
1193   lf->lfCharSet = mfp->charset;\r
1194   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1195   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1196   lf->lfQuality = DEFAULT_QUALITY;\r
1197   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1198     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1199 }\r
1200 \r
1201 void\r
1202 CreateFontInMF(MyFont *mf)\r
1203\r
1204   LFfromMFP(&mf->lf, &mf->mfp);\r
1205   if (mf->hf) DeleteObject(mf->hf);\r
1206   mf->hf = CreateFontIndirect(&mf->lf);\r
1207 }\r
1208 \r
1209 // [HGM] This platform-dependent table provides the location for storing the color info\r
1210 void *\r
1211 colorVariable[] = {\r
1212   &whitePieceColor, \r
1213   &blackPieceColor, \r
1214   &lightSquareColor,\r
1215   &darkSquareColor, \r
1216   &highlightSquareColor,\r
1217   &premoveHighlightColor,\r
1218   NULL,\r
1219   &consoleBackgroundColor,\r
1220   &appData.fontForeColorWhite,\r
1221   &appData.fontBackColorWhite,\r
1222   &appData.fontForeColorBlack,\r
1223   &appData.fontBackColorBlack,\r
1224   &appData.evalHistColorWhite,\r
1225   &appData.evalHistColorBlack,\r
1226   &appData.highlightArrowColor,\r
1227 };\r
1228 \r
1229 /* Command line font name parser.  NULL name means do nothing.\r
1230    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1231    For backward compatibility, syntax without the colon is also\r
1232    accepted, but font names with digits in them won't work in that case.\r
1233 */\r
1234 VOID\r
1235 ParseFontName(char *name, MyFontParams *mfp)\r
1236 {\r
1237   char *p, *q;\r
1238   if (name == NULL) return;\r
1239   p = name;\r
1240   q = strchr(p, ':');\r
1241   if (q) {\r
1242     if (q - p >= sizeof(mfp->faceName))\r
1243       ExitArgError(_("Font name too long:"), name);\r
1244     memcpy(mfp->faceName, p, q - p);\r
1245     mfp->faceName[q - p] = NULLCHAR;\r
1246     p = q + 1;\r
1247   } else {\r
1248     q = mfp->faceName;\r
1249     while (*p && !isdigit(*p)) {\r
1250       *q++ = *p++;\r
1251       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1252         ExitArgError(_("Font name too long:"), name);\r
1253     }\r
1254     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1255     *q = NULLCHAR;\r
1256   }\r
1257   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1258   mfp->pointSize = (float) atof(p);\r
1259   mfp->bold = (strchr(p, 'b') != NULL);\r
1260   mfp->italic = (strchr(p, 'i') != NULL);\r
1261   mfp->underline = (strchr(p, 'u') != NULL);\r
1262   mfp->strikeout = (strchr(p, 's') != NULL);\r
1263   mfp->charset = DEFAULT_CHARSET;\r
1264   q = strchr(p, 'c');\r
1265   if (q)\r
1266     mfp->charset = (BYTE) atoi(q+1);\r
1267 }\r
1268 \r
1269 void\r
1270 ParseFont(char *name, int number)\r
1271 { // wrapper to shield back-end from 'font'\r
1272   ParseFontName(name, &font[boardSize][number]->mfp);\r
1273 }\r
1274 \r
1275 void\r
1276 SetFontDefaults()\r
1277 { // in WB  we have a 2D array of fonts; this initializes their description\r
1278   int i, j;\r
1279   /* Point font array elements to structures and\r
1280      parse default font names */\r
1281   for (i=0; i<NUM_FONTS; i++) {\r
1282     for (j=0; j<NUM_SIZES; j++) {\r
1283       font[j][i] = &fontRec[j][i];\r
1284       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1285     }\r
1286   }\r
1287 }\r
1288 \r
1289 void\r
1290 CreateFonts()\r
1291 { // here we create the actual fonts from the selected descriptions\r
1292   int i, j;\r
1293   for (i=0; i<NUM_FONTS; i++) {\r
1294     for (j=0; j<NUM_SIZES; j++) {\r
1295       CreateFontInMF(font[j][i]);\r
1296     }\r
1297   }\r
1298 }\r
1299 /* Color name parser.\r
1300    X version accepts X color names, but this one\r
1301    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1302 COLORREF\r
1303 ParseColorName(char *name)\r
1304 {\r
1305   int red, green, blue, count;\r
1306   char buf[MSG_SIZ];\r
1307 \r
1308   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1309   if (count != 3) {\r
1310     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1311       &red, &green, &blue);\r
1312   }\r
1313   if (count != 3) {\r
1314     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1315     DisplayError(buf, 0);\r
1316     return RGB(0, 0, 0);\r
1317   }\r
1318   return PALETTERGB(red, green, blue);\r
1319 }\r
1320 \r
1321 void\r
1322 ParseColor(int n, char *name)\r
1323 { // for WinBoard the color is an int, which needs to be derived from the string\r
1324   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1325 }\r
1326 \r
1327 void\r
1328 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1329 {\r
1330   char *e = argValue;\r
1331   int eff = 0;\r
1332 \r
1333   while (*e) {\r
1334     if (*e == 'b')      eff |= CFE_BOLD;\r
1335     else if (*e == 'i') eff |= CFE_ITALIC;\r
1336     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1337     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1338     else if (*e == '#' || isdigit(*e)) break;\r
1339     e++;\r
1340   }\r
1341   *effects = eff;\r
1342   *color   = ParseColorName(e);\r
1343 }\r
1344 \r
1345 void\r
1346 ParseTextAttribs(ColorClass cc, char *s)\r
1347 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1348     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1349     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1350 }\r
1351 \r
1352 void\r
1353 ParseBoardSize(void *addr, char *name)\r
1354 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1355   BoardSize bs = SizeTiny;\r
1356   while (sizeInfo[bs].name != NULL) {\r
1357     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1358         *(BoardSize *)addr = bs;\r
1359         return;\r
1360     }\r
1361     bs++;\r
1362   }\r
1363   ExitArgError(_("Unrecognized board size value"), name);\r
1364 }\r
1365 \r
1366 void\r
1367 LoadAllSounds()\r
1368 { // [HGM] import name from appData first\r
1369   ColorClass cc;\r
1370   SoundClass sc;\r
1371   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1372     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1373     textAttribs[cc].sound.data = NULL;\r
1374     MyLoadSound(&textAttribs[cc].sound);\r
1375   }\r
1376   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1377     textAttribs[cc].sound.name = strdup("");\r
1378     textAttribs[cc].sound.data = NULL;\r
1379   }\r
1380   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1381     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1382     sounds[sc].data = NULL;\r
1383     MyLoadSound(&sounds[sc]);\r
1384   }\r
1385 }\r
1386 \r
1387 void\r
1388 SetCommPortDefaults()\r
1389 {\r
1390    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1391   dcb.DCBlength = sizeof(DCB);\r
1392   dcb.BaudRate = 9600;\r
1393   dcb.fBinary = TRUE;\r
1394   dcb.fParity = FALSE;\r
1395   dcb.fOutxCtsFlow = FALSE;\r
1396   dcb.fOutxDsrFlow = FALSE;\r
1397   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1398   dcb.fDsrSensitivity = FALSE;\r
1399   dcb.fTXContinueOnXoff = TRUE;\r
1400   dcb.fOutX = FALSE;\r
1401   dcb.fInX = FALSE;\r
1402   dcb.fNull = FALSE;\r
1403   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1404   dcb.fAbortOnError = FALSE;\r
1405   dcb.ByteSize = 7;\r
1406   dcb.Parity = SPACEPARITY;\r
1407   dcb.StopBits = ONESTOPBIT;\r
1408 }\r
1409 \r
1410 // [HGM] args: these three cases taken out to stay in front-end\r
1411 void\r
1412 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1413 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1414         // while the curent board size determines the element. This system should be ported to XBoard.\r
1415         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1416         int bs;\r
1417         for (bs=0; bs<NUM_SIZES; bs++) {\r
1418           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1419           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1420           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1421             ad->argName, mfp->faceName, mfp->pointSize,\r
1422             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1423             mfp->bold ? "b" : "",\r
1424             mfp->italic ? "i" : "",\r
1425             mfp->underline ? "u" : "",\r
1426             mfp->strikeout ? "s" : "",\r
1427             (int)mfp->charset);\r
1428         }\r
1429       }\r
1430 \r
1431 void\r
1432 ExportSounds()\r
1433 { // [HGM] copy the names from the internal WB variables to appData\r
1434   ColorClass cc;\r
1435   SoundClass sc;\r
1436   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1437     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1438   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1439     (&appData.soundMove)[sc] = sounds[sc].name;\r
1440 }\r
1441 \r
1442 void\r
1443 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1444 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1445         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1446         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1447           (ta->effects & CFE_BOLD) ? "b" : "",\r
1448           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1449           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1450           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1451           (ta->effects) ? " " : "",\r
1452           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1453       }\r
1454 \r
1455 void\r
1456 SaveColor(FILE *f, ArgDescriptor *ad)\r
1457 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1458         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1459         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1460           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1461 }\r
1462 \r
1463 void\r
1464 SaveBoardSize(FILE *f, char *name, void *addr)\r
1465 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1466   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1467 }\r
1468 \r
1469 void\r
1470 ParseCommPortSettings(char *s)\r
1471 { // wrapper to keep dcb from back-end\r
1472   ParseCommSettings(s, &dcb);\r
1473 }\r
1474 \r
1475 void\r
1476 GetWindowCoords()\r
1477 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1478   GetActualPlacement(hwndMain, &wpMain);\r
1479   GetActualPlacement(hwndConsole, &wpConsole);\r
1480   GetActualPlacement(commentDialog, &wpComment);\r
1481   GetActualPlacement(editTagsDialog, &wpTags);\r
1482   GetActualPlacement(gameListDialog, &wpGameList);\r
1483   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1484   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1485   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1486 }\r
1487 \r
1488 void\r
1489 PrintCommPortSettings(FILE *f, char *name)\r
1490 { // wrapper to shield back-end from DCB\r
1491       PrintCommSettings(f, name, &dcb);\r
1492 }\r
1493 \r
1494 int\r
1495 MySearchPath(char *installDir, char *name, char *fullname)\r
1496 {\r
1497   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1498   if(name[0]== '%') {\r
1499     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1500     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1501       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1502       *strchr(buf, '%') = 0;\r
1503       strcat(fullname, getenv(buf));\r
1504       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1505     }\r
1506     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1507     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1508     return (int) strlen(fullname);\r
1509   }\r
1510   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1511 }\r
1512 \r
1513 int\r
1514 MyGetFullPathName(char *name, char *fullname)\r
1515 {\r
1516   char *dummy;\r
1517   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1518 }\r
1519 \r
1520 int\r
1521 MainWindowUp()\r
1522 { // [HGM] args: allows testing if main window is realized from back-end\r
1523   return hwndMain != NULL;\r
1524 }\r
1525 \r
1526 void\r
1527 PopUpStartupDialog()\r
1528 {\r
1529     FARPROC lpProc;\r
1530     \r
1531     LoadLanguageFile(appData.language);\r
1532     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1533     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1534     FreeProcInstance(lpProc);\r
1535 }\r
1536 \r
1537 /*---------------------------------------------------------------------------*\\r
1538  *\r
1539  * GDI board drawing routines\r
1540  *\r
1541 \*---------------------------------------------------------------------------*/\r
1542 \r
1543 /* [AS] Draw square using background texture */\r
1544 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1545 {\r
1546     XFORM   x;\r
1547 \r
1548     if( mode == 0 ) {\r
1549         return; /* Should never happen! */\r
1550     }\r
1551 \r
1552     SetGraphicsMode( dst, GM_ADVANCED );\r
1553 \r
1554     switch( mode ) {\r
1555     case 1:\r
1556         /* Identity */\r
1557         break;\r
1558     case 2:\r
1559         /* X reflection */\r
1560         x.eM11 = -1.0;\r
1561         x.eM12 = 0;\r
1562         x.eM21 = 0;\r
1563         x.eM22 = 1.0;\r
1564         x.eDx = (FLOAT) dw + dx - 1;\r
1565         x.eDy = 0;\r
1566         dx = 0;\r
1567         SetWorldTransform( dst, &x );\r
1568         break;\r
1569     case 3:\r
1570         /* Y reflection */\r
1571         x.eM11 = 1.0;\r
1572         x.eM12 = 0;\r
1573         x.eM21 = 0;\r
1574         x.eM22 = -1.0;\r
1575         x.eDx = 0;\r
1576         x.eDy = (FLOAT) dh + dy - 1;\r
1577         dy = 0;\r
1578         SetWorldTransform( dst, &x );\r
1579         break;\r
1580     case 4:\r
1581         /* X/Y flip */\r
1582         x.eM11 = 0;\r
1583         x.eM12 = 1.0;\r
1584         x.eM21 = 1.0;\r
1585         x.eM22 = 0;\r
1586         x.eDx = (FLOAT) dx;\r
1587         x.eDy = (FLOAT) dy;\r
1588         dx = 0;\r
1589         dy = 0;\r
1590         SetWorldTransform( dst, &x );\r
1591         break;\r
1592     }\r
1593 \r
1594     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1595 \r
1596     x.eM11 = 1.0;\r
1597     x.eM12 = 0;\r
1598     x.eM21 = 0;\r
1599     x.eM22 = 1.0;\r
1600     x.eDx = 0;\r
1601     x.eDy = 0;\r
1602     SetWorldTransform( dst, &x );\r
1603 \r
1604     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1605 }\r
1606 \r
1607 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1608 enum {\r
1609     PM_WP = (int) WhitePawn, \r
1610     PM_WN = (int) WhiteKnight, \r
1611     PM_WB = (int) WhiteBishop, \r
1612     PM_WR = (int) WhiteRook, \r
1613     PM_WQ = (int) WhiteQueen, \r
1614     PM_WF = (int) WhiteFerz, \r
1615     PM_WW = (int) WhiteWazir, \r
1616     PM_WE = (int) WhiteAlfil, \r
1617     PM_WM = (int) WhiteMan, \r
1618     PM_WO = (int) WhiteCannon, \r
1619     PM_WU = (int) WhiteUnicorn, \r
1620     PM_WH = (int) WhiteNightrider, \r
1621     PM_WA = (int) WhiteAngel, \r
1622     PM_WC = (int) WhiteMarshall, \r
1623     PM_WAB = (int) WhiteCardinal, \r
1624     PM_WD = (int) WhiteDragon, \r
1625     PM_WL = (int) WhiteLance, \r
1626     PM_WS = (int) WhiteCobra, \r
1627     PM_WV = (int) WhiteFalcon, \r
1628     PM_WSG = (int) WhiteSilver, \r
1629     PM_WG = (int) WhiteGrasshopper, \r
1630     PM_WK = (int) WhiteKing,\r
1631     PM_BP = (int) BlackPawn, \r
1632     PM_BN = (int) BlackKnight, \r
1633     PM_BB = (int) BlackBishop, \r
1634     PM_BR = (int) BlackRook, \r
1635     PM_BQ = (int) BlackQueen, \r
1636     PM_BF = (int) BlackFerz, \r
1637     PM_BW = (int) BlackWazir, \r
1638     PM_BE = (int) BlackAlfil, \r
1639     PM_BM = (int) BlackMan,\r
1640     PM_BO = (int) BlackCannon, \r
1641     PM_BU = (int) BlackUnicorn, \r
1642     PM_BH = (int) BlackNightrider, \r
1643     PM_BA = (int) BlackAngel, \r
1644     PM_BC = (int) BlackMarshall, \r
1645     PM_BG = (int) BlackGrasshopper, \r
1646     PM_BAB = (int) BlackCardinal,\r
1647     PM_BD = (int) BlackDragon,\r
1648     PM_BL = (int) BlackLance,\r
1649     PM_BS = (int) BlackCobra,\r
1650     PM_BV = (int) BlackFalcon,\r
1651     PM_BSG = (int) BlackSilver,\r
1652     PM_BK = (int) BlackKing\r
1653 };\r
1654 \r
1655 static HFONT hPieceFont = NULL;\r
1656 static HBITMAP hPieceMask[(int) EmptySquare];\r
1657 static HBITMAP hPieceFace[(int) EmptySquare];\r
1658 static int fontBitmapSquareSize = 0;\r
1659 static char pieceToFontChar[(int) EmptySquare] =\r
1660                               { 'p', 'n', 'b', 'r', 'q', \r
1661                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1662                       'k', 'o', 'm', 'v', 't', 'w', \r
1663                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1664                                                               'l' };\r
1665 \r
1666 extern BOOL SetCharTable( char *table, const char * map );\r
1667 /* [HGM] moved to backend.c */\r
1668 \r
1669 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1670 {\r
1671     HBRUSH hbrush;\r
1672     BYTE r1 = GetRValue( color );\r
1673     BYTE g1 = GetGValue( color );\r
1674     BYTE b1 = GetBValue( color );\r
1675     BYTE r2 = r1 / 2;\r
1676     BYTE g2 = g1 / 2;\r
1677     BYTE b2 = b1 / 2;\r
1678     RECT rc;\r
1679 \r
1680     /* Create a uniform background first */\r
1681     hbrush = CreateSolidBrush( color );\r
1682     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1683     FillRect( hdc, &rc, hbrush );\r
1684     DeleteObject( hbrush );\r
1685     \r
1686     if( mode == 1 ) {\r
1687         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1688         int steps = squareSize / 2;\r
1689         int i;\r
1690 \r
1691         for( i=0; i<steps; i++ ) {\r
1692             BYTE r = r1 - (r1-r2) * i / steps;\r
1693             BYTE g = g1 - (g1-g2) * i / steps;\r
1694             BYTE b = b1 - (b1-b2) * i / steps;\r
1695 \r
1696             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1697             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1698             FillRect( hdc, &rc, hbrush );\r
1699             DeleteObject(hbrush);\r
1700         }\r
1701     }\r
1702     else if( mode == 2 ) {\r
1703         /* Diagonal gradient, good more or less for every piece */\r
1704         POINT triangle[3];\r
1705         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1706         HBRUSH hbrush_old;\r
1707         int steps = squareSize;\r
1708         int i;\r
1709 \r
1710         triangle[0].x = squareSize - steps;\r
1711         triangle[0].y = squareSize;\r
1712         triangle[1].x = squareSize;\r
1713         triangle[1].y = squareSize;\r
1714         triangle[2].x = squareSize;\r
1715         triangle[2].y = squareSize - steps;\r
1716 \r
1717         for( i=0; i<steps; i++ ) {\r
1718             BYTE r = r1 - (r1-r2) * i / steps;\r
1719             BYTE g = g1 - (g1-g2) * i / steps;\r
1720             BYTE b = b1 - (b1-b2) * i / steps;\r
1721 \r
1722             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1723             hbrush_old = SelectObject( hdc, hbrush );\r
1724             Polygon( hdc, triangle, 3 );\r
1725             SelectObject( hdc, hbrush_old );\r
1726             DeleteObject(hbrush);\r
1727             triangle[0].x++;\r
1728             triangle[2].y++;\r
1729         }\r
1730 \r
1731         SelectObject( hdc, hpen );\r
1732     }\r
1733 }\r
1734 \r
1735 /*\r
1736     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1737     seems to work ok. The main problem here is to find the "inside" of a chess\r
1738     piece: follow the steps as explained below.\r
1739 */\r
1740 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1741 {\r
1742     HBITMAP hbm;\r
1743     HBITMAP hbm_old;\r
1744     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1745     RECT rc;\r
1746     SIZE sz;\r
1747     POINT pt;\r
1748     int backColor = whitePieceColor; \r
1749     int foreColor = blackPieceColor;\r
1750     \r
1751     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1752         backColor = appData.fontBackColorWhite;\r
1753         foreColor = appData.fontForeColorWhite;\r
1754     }\r
1755     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1756         backColor = appData.fontBackColorBlack;\r
1757         foreColor = appData.fontForeColorBlack;\r
1758     }\r
1759 \r
1760     /* Mask */\r
1761     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1762 \r
1763     hbm_old = SelectObject( hdc, hbm );\r
1764 \r
1765     rc.left = 0;\r
1766     rc.top = 0;\r
1767     rc.right = squareSize;\r
1768     rc.bottom = squareSize;\r
1769 \r
1770     /* Step 1: background is now black */\r
1771     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1772 \r
1773     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1774 \r
1775     pt.x = (squareSize - sz.cx) / 2;\r
1776     pt.y = (squareSize - sz.cy) / 2;\r
1777 \r
1778     SetBkMode( hdc, TRANSPARENT );\r
1779     SetTextColor( hdc, chroma );\r
1780     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1781     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1782 \r
1783     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1784     /* Step 3: the area outside the piece is filled with white */\r
1785 //    FloodFill( hdc, 0, 0, chroma );\r
1786     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1787     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1788     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1789     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1790     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1791     /* \r
1792         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1793         but if the start point is not inside the piece we're lost!\r
1794         There should be a better way to do this... if we could create a region or path\r
1795         from the fill operation we would be fine for example.\r
1796     */\r
1797 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1798     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1799 \r
1800     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1801         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1802         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1803 \r
1804         SelectObject( dc2, bm2 );\r
1805         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1806         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1807         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1808         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1809         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1810 \r
1811         DeleteDC( dc2 );\r
1812         DeleteObject( bm2 );\r
1813     }\r
1814 \r
1815     SetTextColor( hdc, 0 );\r
1816     /* \r
1817         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1818         draw the piece again in black for safety.\r
1819     */\r
1820     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1821 \r
1822     SelectObject( hdc, hbm_old );\r
1823 \r
1824     if( hPieceMask[index] != NULL ) {\r
1825         DeleteObject( hPieceMask[index] );\r
1826     }\r
1827 \r
1828     hPieceMask[index] = hbm;\r
1829 \r
1830     /* Face */\r
1831     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1832 \r
1833     SelectObject( hdc, hbm );\r
1834 \r
1835     {\r
1836         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1837         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1838         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1839 \r
1840         SelectObject( dc1, hPieceMask[index] );\r
1841         SelectObject( dc2, bm2 );\r
1842         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1843         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1844         \r
1845         /* \r
1846             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1847             the piece background and deletes (makes transparent) the rest.\r
1848             Thanks to that mask, we are free to paint the background with the greates\r
1849             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1850             We use this, to make gradients and give the pieces a "roundish" look.\r
1851         */\r
1852         SetPieceBackground( hdc, backColor, 2 );\r
1853         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1854 \r
1855         DeleteDC( dc2 );\r
1856         DeleteDC( dc1 );\r
1857         DeleteObject( bm2 );\r
1858     }\r
1859 \r
1860     SetTextColor( hdc, foreColor );\r
1861     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1862 \r
1863     SelectObject( hdc, hbm_old );\r
1864 \r
1865     if( hPieceFace[index] != NULL ) {\r
1866         DeleteObject( hPieceFace[index] );\r
1867     }\r
1868 \r
1869     hPieceFace[index] = hbm;\r
1870 }\r
1871 \r
1872 static int TranslatePieceToFontPiece( int piece )\r
1873 {\r
1874     switch( piece ) {\r
1875     case BlackPawn:\r
1876         return PM_BP;\r
1877     case BlackKnight:\r
1878         return PM_BN;\r
1879     case BlackBishop:\r
1880         return PM_BB;\r
1881     case BlackRook:\r
1882         return PM_BR;\r
1883     case BlackQueen:\r
1884         return PM_BQ;\r
1885     case BlackKing:\r
1886         return PM_BK;\r
1887     case WhitePawn:\r
1888         return PM_WP;\r
1889     case WhiteKnight:\r
1890         return PM_WN;\r
1891     case WhiteBishop:\r
1892         return PM_WB;\r
1893     case WhiteRook:\r
1894         return PM_WR;\r
1895     case WhiteQueen:\r
1896         return PM_WQ;\r
1897     case WhiteKing:\r
1898         return PM_WK;\r
1899 \r
1900     case BlackAngel:\r
1901         return PM_BA;\r
1902     case BlackMarshall:\r
1903         return PM_BC;\r
1904     case BlackFerz:\r
1905         return PM_BF;\r
1906     case BlackNightrider:\r
1907         return PM_BH;\r
1908     case BlackAlfil:\r
1909         return PM_BE;\r
1910     case BlackWazir:\r
1911         return PM_BW;\r
1912     case BlackUnicorn:\r
1913         return PM_BU;\r
1914     case BlackCannon:\r
1915         return PM_BO;\r
1916     case BlackGrasshopper:\r
1917         return PM_BG;\r
1918     case BlackMan:\r
1919         return PM_BM;\r
1920     case BlackSilver:\r
1921         return PM_BSG;\r
1922     case BlackLance:\r
1923         return PM_BL;\r
1924     case BlackFalcon:\r
1925         return PM_BV;\r
1926     case BlackCobra:\r
1927         return PM_BS;\r
1928     case BlackCardinal:\r
1929         return PM_BAB;\r
1930     case BlackDragon:\r
1931         return PM_BD;\r
1932 \r
1933     case WhiteAngel:\r
1934         return PM_WA;\r
1935     case WhiteMarshall:\r
1936         return PM_WC;\r
1937     case WhiteFerz:\r
1938         return PM_WF;\r
1939     case WhiteNightrider:\r
1940         return PM_WH;\r
1941     case WhiteAlfil:\r
1942         return PM_WE;\r
1943     case WhiteWazir:\r
1944         return PM_WW;\r
1945     case WhiteUnicorn:\r
1946         return PM_WU;\r
1947     case WhiteCannon:\r
1948         return PM_WO;\r
1949     case WhiteGrasshopper:\r
1950         return PM_WG;\r
1951     case WhiteMan:\r
1952         return PM_WM;\r
1953     case WhiteSilver:\r
1954         return PM_WSG;\r
1955     case WhiteLance:\r
1956         return PM_WL;\r
1957     case WhiteFalcon:\r
1958         return PM_WV;\r
1959     case WhiteCobra:\r
1960         return PM_WS;\r
1961     case WhiteCardinal:\r
1962         return PM_WAB;\r
1963     case WhiteDragon:\r
1964         return PM_WD;\r
1965     }\r
1966 \r
1967     return 0;\r
1968 }\r
1969 \r
1970 void CreatePiecesFromFont()\r
1971 {\r
1972     LOGFONT lf;\r
1973     HDC hdc_window = NULL;\r
1974     HDC hdc = NULL;\r
1975     HFONT hfont_old;\r
1976     int fontHeight;\r
1977     int i;\r
1978 \r
1979     if( fontBitmapSquareSize < 0 ) {\r
1980         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1981         return;\r
1982     }\r
1983 \r
1984     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1985         fontBitmapSquareSize = -1;\r
1986         return;\r
1987     }\r
1988 \r
1989     if( fontBitmapSquareSize != squareSize ) {\r
1990         hdc_window = GetDC( hwndMain );\r
1991         hdc = CreateCompatibleDC( hdc_window );\r
1992 \r
1993         if( hPieceFont != NULL ) {\r
1994             DeleteObject( hPieceFont );\r
1995         }\r
1996         else {\r
1997             for( i=0; i<=(int)BlackKing; i++ ) {\r
1998                 hPieceMask[i] = NULL;\r
1999                 hPieceFace[i] = NULL;\r
2000             }\r
2001         }\r
2002 \r
2003         fontHeight = 75;\r
2004 \r
2005         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2006             fontHeight = appData.fontPieceSize;\r
2007         }\r
2008 \r
2009         fontHeight = (fontHeight * squareSize) / 100;\r
2010 \r
2011         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2012         lf.lfWidth = 0;\r
2013         lf.lfEscapement = 0;\r
2014         lf.lfOrientation = 0;\r
2015         lf.lfWeight = FW_NORMAL;\r
2016         lf.lfItalic = 0;\r
2017         lf.lfUnderline = 0;\r
2018         lf.lfStrikeOut = 0;\r
2019         lf.lfCharSet = DEFAULT_CHARSET;\r
2020         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2021         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2022         lf.lfQuality = PROOF_QUALITY;\r
2023         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2024         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2025         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2026 \r
2027         hPieceFont = CreateFontIndirect( &lf );\r
2028 \r
2029         if( hPieceFont == NULL ) {\r
2030             fontBitmapSquareSize = -2;\r
2031         }\r
2032         else {\r
2033             /* Setup font-to-piece character table */\r
2034             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2035                 /* No (or wrong) global settings, try to detect the font */\r
2036                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2037                     /* Alpha */\r
2038                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2039                 }\r
2040                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2041                     /* DiagramTT* family */\r
2042                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2043                 }\r
2044                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2045                     /* Fairy symbols */\r
2046                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2047                 }\r
2048                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2049                     /* Good Companion (Some characters get warped as literal :-( */\r
2050                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2051                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2052                     SetCharTable(pieceToFontChar, s);\r
2053                 }\r
2054                 else {\r
2055                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2056                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2057                 }\r
2058             }\r
2059 \r
2060             /* Create bitmaps */\r
2061             hfont_old = SelectObject( hdc, hPieceFont );\r
2062             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2063                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2064                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2065 \r
2066             SelectObject( hdc, hfont_old );\r
2067 \r
2068             fontBitmapSquareSize = squareSize;\r
2069         }\r
2070     }\r
2071 \r
2072     if( hdc != NULL ) {\r
2073         DeleteDC( hdc );\r
2074     }\r
2075 \r
2076     if( hdc_window != NULL ) {\r
2077         ReleaseDC( hwndMain, hdc_window );\r
2078     }\r
2079 }\r
2080 \r
2081 HBITMAP\r
2082 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2083 {\r
2084   char name[128];\r
2085 \r
2086     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2087   if (gameInfo.event &&\r
2088       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2089       strcmp(name, "k80s") == 0) {\r
2090     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2091   }\r
2092   return LoadBitmap(hinst, name);\r
2093 }\r
2094 \r
2095 \r
2096 /* Insert a color into the program's logical palette\r
2097    structure.  This code assumes the given color is\r
2098    the result of the RGB or PALETTERGB macro, and it\r
2099    knows how those macros work (which is documented).\r
2100 */\r
2101 VOID\r
2102 InsertInPalette(COLORREF color)\r
2103 {\r
2104   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2105 \r
2106   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2107     DisplayFatalError(_("Too many colors"), 0, 1);\r
2108     pLogPal->palNumEntries--;\r
2109     return;\r
2110   }\r
2111 \r
2112   pe->peFlags = (char) 0;\r
2113   pe->peRed = (char) (0xFF & color);\r
2114   pe->peGreen = (char) (0xFF & (color >> 8));\r
2115   pe->peBlue = (char) (0xFF & (color >> 16));\r
2116   return;\r
2117 }\r
2118 \r
2119 \r
2120 VOID\r
2121 InitDrawingColors()\r
2122 {\r
2123   if (pLogPal == NULL) {\r
2124     /* Allocate enough memory for a logical palette with\r
2125      * PALETTESIZE entries and set the size and version fields\r
2126      * of the logical palette structure.\r
2127      */\r
2128     pLogPal = (NPLOGPALETTE)\r
2129       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2130                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2131     pLogPal->palVersion    = 0x300;\r
2132   }\r
2133   pLogPal->palNumEntries = 0;\r
2134 \r
2135   InsertInPalette(lightSquareColor);\r
2136   InsertInPalette(darkSquareColor);\r
2137   InsertInPalette(whitePieceColor);\r
2138   InsertInPalette(blackPieceColor);\r
2139   InsertInPalette(highlightSquareColor);\r
2140   InsertInPalette(premoveHighlightColor);\r
2141 \r
2142   /*  create a logical color palette according the information\r
2143    *  in the LOGPALETTE structure.\r
2144    */\r
2145   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2146 \r
2147   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2148   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2149   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2150   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2151   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2152   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2153   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2154   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2155   /* [AS] Force rendering of the font-based pieces */\r
2156   if( fontBitmapSquareSize > 0 ) {\r
2157     fontBitmapSquareSize = 0;\r
2158   }\r
2159 }\r
2160 \r
2161 \r
2162 int\r
2163 BoardWidth(int boardSize, int n)\r
2164 { /* [HGM] argument n added to allow different width and height */\r
2165   int lineGap = sizeInfo[boardSize].lineGap;\r
2166 \r
2167   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2168       lineGap = appData.overrideLineGap;\r
2169   }\r
2170 \r
2171   return (n + 1) * lineGap +\r
2172           n * sizeInfo[boardSize].squareSize;\r
2173 }\r
2174 \r
2175 /* Respond to board resize by dragging edge */\r
2176 VOID\r
2177 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2178 {\r
2179   BoardSize newSize = NUM_SIZES - 1;\r
2180   static int recurse = 0;\r
2181   if (IsIconic(hwndMain)) return;\r
2182   if (recurse > 0) return;\r
2183   recurse++;\r
2184   while (newSize > 0) {\r
2185         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2186         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2187            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2188     newSize--;\r
2189   } \r
2190   boardSize = newSize;\r
2191   InitDrawingSizes(boardSize, flags);\r
2192   recurse--;\r
2193 }\r
2194 \r
2195 \r
2196 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2197 \r
2198 VOID\r
2199 InitDrawingSizes(BoardSize boardSize, int flags)\r
2200 {\r
2201   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2202   ChessSquare piece;\r
2203   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2204   HDC hdc;\r
2205   SIZE clockSize, messageSize;\r
2206   HFONT oldFont;\r
2207   char buf[MSG_SIZ];\r
2208   char *str;\r
2209   HMENU hmenu = GetMenu(hwndMain);\r
2210   RECT crect, wrect, oldRect;\r
2211   int offby;\r
2212   LOGBRUSH logbrush;\r
2213 \r
2214   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2215   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2216 \r
2217   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2218   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2219 \r
2220   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2221   oldRect.top = wpMain.y;\r
2222   oldRect.right = wpMain.x + wpMain.width;\r
2223   oldRect.bottom = wpMain.y + wpMain.height;\r
2224 \r
2225   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2226   smallLayout = sizeInfo[boardSize].smallLayout;\r
2227   squareSize = sizeInfo[boardSize].squareSize;\r
2228   lineGap = sizeInfo[boardSize].lineGap;\r
2229   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2230 \r
2231   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2232       lineGap = appData.overrideLineGap;\r
2233   }\r
2234 \r
2235   if (tinyLayout != oldTinyLayout) {\r
2236     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2237     if (tinyLayout) {\r
2238       style &= ~WS_SYSMENU;\r
2239       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2240                  "&Minimize\tCtrl+F4");\r
2241     } else {\r
2242       style |= WS_SYSMENU;\r
2243       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2244     }\r
2245     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2246 \r
2247     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2248       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2249         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2250     }\r
2251     DrawMenuBar(hwndMain);\r
2252   }\r
2253 \r
2254   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2255   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2256 \r
2257   /* Get text area sizes */\r
2258   hdc = GetDC(hwndMain);\r
2259   if (appData.clockMode) {\r
2260     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2261   } else {\r
2262     snprintf(buf, MSG_SIZ, _("White"));\r
2263   }\r
2264   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2265   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2266   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2267   str = _("We only care about the height here");\r
2268   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2269   SelectObject(hdc, oldFont);\r
2270   ReleaseDC(hwndMain, hdc);\r
2271 \r
2272   /* Compute where everything goes */\r
2273   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2274         /* [HGM] logo: if either logo is on, reserve space for it */\r
2275         logoHeight =  2*clockSize.cy;\r
2276         leftLogoRect.left   = OUTER_MARGIN;\r
2277         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2278         leftLogoRect.top    = OUTER_MARGIN;\r
2279         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2280 \r
2281         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2282         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2283         rightLogoRect.top    = OUTER_MARGIN;\r
2284         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2285 \r
2286 \r
2287     whiteRect.left = leftLogoRect.right;\r
2288     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2289     whiteRect.top = OUTER_MARGIN;\r
2290     whiteRect.bottom = whiteRect.top + logoHeight;\r
2291 \r
2292     blackRect.right = rightLogoRect.left;\r
2293     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2294     blackRect.top = whiteRect.top;\r
2295     blackRect.bottom = whiteRect.bottom;\r
2296   } else {\r
2297     whiteRect.left = OUTER_MARGIN;\r
2298     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2299     whiteRect.top = OUTER_MARGIN;\r
2300     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2301 \r
2302     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2303     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2304     blackRect.top = whiteRect.top;\r
2305     blackRect.bottom = whiteRect.bottom;\r
2306 \r
2307     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2308   }\r
2309 \r
2310   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2311   if (appData.showButtonBar) {\r
2312     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2313       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2314   } else {\r
2315     messageRect.right = OUTER_MARGIN + boardWidth;\r
2316   }\r
2317   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2318   messageRect.bottom = messageRect.top + messageSize.cy;\r
2319 \r
2320   boardRect.left = OUTER_MARGIN;\r
2321   boardRect.right = boardRect.left + boardWidth;\r
2322   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2323   boardRect.bottom = boardRect.top + boardHeight;\r
2324 \r
2325   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2326   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2327   oldBoardSize = boardSize;\r
2328   oldTinyLayout = tinyLayout;\r
2329   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2330   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2331     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2332   winW *= 1 + twoBoards;\r
2333   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2334   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2335   wpMain.height = winH; //       without disturbing window attachments\r
2336   GetWindowRect(hwndMain, &wrect);\r
2337   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2338                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2339 \r
2340   // [HGM] placement: let attached windows follow size change.\r
2341   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2342   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2343   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2344   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2345   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2346 \r
2347   /* compensate if menu bar wrapped */\r
2348   GetClientRect(hwndMain, &crect);\r
2349   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2350   wpMain.height += offby;\r
2351   switch (flags) {\r
2352   case WMSZ_TOPLEFT:\r
2353     SetWindowPos(hwndMain, NULL, \r
2354                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2355                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2356     break;\r
2357 \r
2358   case WMSZ_TOPRIGHT:\r
2359   case WMSZ_TOP:\r
2360     SetWindowPos(hwndMain, NULL, \r
2361                  wrect.left, wrect.bottom - wpMain.height, \r
2362                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2363     break;\r
2364 \r
2365   case WMSZ_BOTTOMLEFT:\r
2366   case WMSZ_LEFT:\r
2367     SetWindowPos(hwndMain, NULL, \r
2368                  wrect.right - wpMain.width, wrect.top, \r
2369                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2370     break;\r
2371 \r
2372   case WMSZ_BOTTOMRIGHT:\r
2373   case WMSZ_BOTTOM:\r
2374   case WMSZ_RIGHT:\r
2375   default:\r
2376     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2377                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2378     break;\r
2379   }\r
2380 \r
2381   hwndPause = NULL;\r
2382   for (i = 0; i < N_BUTTONS; i++) {\r
2383     if (buttonDesc[i].hwnd != NULL) {\r
2384       DestroyWindow(buttonDesc[i].hwnd);\r
2385       buttonDesc[i].hwnd = NULL;\r
2386     }\r
2387     if (appData.showButtonBar) {\r
2388       buttonDesc[i].hwnd =\r
2389         CreateWindow("BUTTON", buttonDesc[i].label,\r
2390                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2391                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2392                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2393                      (HMENU) buttonDesc[i].id,\r
2394                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2395       if (tinyLayout) {\r
2396         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2397                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2398                     MAKELPARAM(FALSE, 0));\r
2399       }\r
2400       if (buttonDesc[i].id == IDM_Pause)\r
2401         hwndPause = buttonDesc[i].hwnd;\r
2402       buttonDesc[i].wndproc = (WNDPROC)\r
2403         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2404     }\r
2405   }\r
2406   if (gridPen != NULL) DeleteObject(gridPen);\r
2407   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2408   if (premovePen != NULL) DeleteObject(premovePen);\r
2409   if (lineGap != 0) {\r
2410     logbrush.lbStyle = BS_SOLID;\r
2411     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2412     gridPen =\r
2413       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2414                    lineGap, &logbrush, 0, NULL);\r
2415     logbrush.lbColor = highlightSquareColor;\r
2416     highlightPen =\r
2417       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2418                    lineGap, &logbrush, 0, NULL);\r
2419 \r
2420     logbrush.lbColor = premoveHighlightColor; \r
2421     premovePen =\r
2422       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2423                    lineGap, &logbrush, 0, NULL);\r
2424 \r
2425     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2426     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2427       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2428       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2429         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2430       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2431         BOARD_WIDTH * (squareSize + lineGap);\r
2432       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2433     }\r
2434     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2435       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2436       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2437         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2438         lineGap / 2 + (i * (squareSize + lineGap));\r
2439       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2440         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2441       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2442     }\r
2443   }\r
2444 \r
2445   /* [HGM] Licensing requirement */\r
2446 #ifdef GOTHIC\r
2447   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2448 #endif\r
2449 #ifdef FALCON\r
2450   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2451 #endif\r
2452   GothicPopUp( "", VariantNormal);\r
2453 \r
2454 \r
2455 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2456 \r
2457   /* Load piece bitmaps for this board size */\r
2458   for (i=0; i<=2; i++) {\r
2459     for (piece = WhitePawn;\r
2460          (int) piece < (int) BlackPawn;\r
2461          piece = (ChessSquare) ((int) piece + 1)) {\r
2462       if (pieceBitmap[i][piece] != NULL)\r
2463         DeleteObject(pieceBitmap[i][piece]);\r
2464     }\r
2465   }\r
2466 \r
2467   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2468   // Orthodox Chess pieces\r
2469   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2470   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2471   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2472   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2473   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2474   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2475   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2476   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2477   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2478   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2479   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2480   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2481   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2482   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2483   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2484   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2485     // in Shogi, Hijack the unused Queen for Lance\r
2486     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2487     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2488     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2489   } else {\r
2490     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2491     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2492     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2493   }\r
2494 \r
2495   if(squareSize <= 72 && squareSize >= 33) { \r
2496     /* A & C are available in most sizes now */\r
2497     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2498       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2499       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2500       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2501       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2502       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2503       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2504       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2505       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2506       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2507       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2508       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2509       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2510     } else { // Smirf-like\r
2511       if(gameInfo.variant == VariantSChess) {\r
2512         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2513         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2514         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2515       } else {\r
2516         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2517         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2518         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2519       }\r
2520     }\r
2521     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2522       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2523       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2524       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2525     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2526       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2527       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2528       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2529     } else { // WinBoard standard\r
2530       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2531       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2532       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2533     }\r
2534   }\r
2535 \r
2536 \r
2537   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2538     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2539     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2540     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2541     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2542     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2543     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2544     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2545     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2546     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2547     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2548     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2549     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2550     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2551     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2552     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2553     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2554     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2555     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2556     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2557     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2558     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2559     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2560     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2561     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2562     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2563     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2564     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2565     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2566     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2567     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2568 \r
2569     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2570       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2571       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2572       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2573       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2574       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2575       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2576       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2577       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2578       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2579       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2580       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2581       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2582     } else {\r
2583       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2584       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2585       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2586       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2587       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2588       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2589       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2590       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2591       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2592       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2593       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2594       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2595     }\r
2596 \r
2597   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2598     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2599     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2600     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2601     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2602     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2603     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2604     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2605     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2606     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2607     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2608     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2609     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2610     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2611     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2612   }\r
2613 \r
2614 \r
2615   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2616   /* special Shogi support in this size */\r
2617   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2618       for (piece = WhitePawn;\r
2619            (int) piece < (int) BlackPawn;\r
2620            piece = (ChessSquare) ((int) piece + 1)) {\r
2621         if (pieceBitmap[i][piece] != NULL)\r
2622           DeleteObject(pieceBitmap[i][piece]);\r
2623       }\r
2624     }\r
2625   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2626   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2627   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2628   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2629   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2630   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2631   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2632   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2633   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2634   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2635   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2636   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2637   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2638   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2639   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2640   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2641   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2642   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2643   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2644   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2645   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2646   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2647   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2648   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2649   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2650   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2651   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2652   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2653   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2654   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2655   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2656   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2657   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2658   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2659   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2660   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2661   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2662   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2663   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2664   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2665   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2666   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2667   minorSize = 0;\r
2668   }\r
2669 }\r
2670 \r
2671 HBITMAP\r
2672 PieceBitmap(ChessSquare p, int kind)\r
2673 {\r
2674   if ((int) p >= (int) BlackPawn)\r
2675     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2676 \r
2677   return pieceBitmap[kind][(int) p];\r
2678 }\r
2679 \r
2680 /***************************************************************/\r
2681 \r
2682 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2683 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2684 /*\r
2685 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2686 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2687 */\r
2688 \r
2689 VOID\r
2690 SquareToPos(int row, int column, int * x, int * y)\r
2691 {\r
2692   if (flipView) {\r
2693     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2694     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2695   } else {\r
2696     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2697     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2698   }\r
2699 }\r
2700 \r
2701 VOID\r
2702 DrawCoordsOnDC(HDC hdc)\r
2703 {\r
2704   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
2705   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
2706   char str[2] = { NULLCHAR, NULLCHAR };\r
2707   int oldMode, oldAlign, x, y, start, i;\r
2708   HFONT oldFont;\r
2709   HBRUSH oldBrush;\r
2710 \r
2711   if (!appData.showCoords)\r
2712     return;\r
2713 \r
2714   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2715 \r
2716   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2717   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2718   oldAlign = GetTextAlign(hdc);\r
2719   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2720 \r
2721   y = boardRect.top + lineGap;\r
2722   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2723 \r
2724   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2725   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2726     str[0] = files[start + i];\r
2727     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2728     y += squareSize + lineGap;\r
2729   }\r
2730 \r
2731   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2732 \r
2733   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2734   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2735     str[0] = ranks[start + i];\r
2736     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2737     x += squareSize + lineGap;\r
2738   }    \r
2739 \r
2740   SelectObject(hdc, oldBrush);\r
2741   SetBkMode(hdc, oldMode);\r
2742   SetTextAlign(hdc, oldAlign);\r
2743   SelectObject(hdc, oldFont);\r
2744 }\r
2745 \r
2746 VOID\r
2747 DrawGridOnDC(HDC hdc)\r
2748 {\r
2749   HPEN oldPen;\r
2750  \r
2751   if (lineGap != 0) {\r
2752     oldPen = SelectObject(hdc, gridPen);\r
2753     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2754     SelectObject(hdc, oldPen);\r
2755   }\r
2756 }\r
2757 \r
2758 #define HIGHLIGHT_PEN 0\r
2759 #define PREMOVE_PEN   1\r
2760 \r
2761 VOID\r
2762 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2763 {\r
2764   int x1, y1;\r
2765   HPEN oldPen, hPen;\r
2766   if (lineGap == 0) return;\r
2767   if (flipView) {\r
2768     x1 = boardRect.left +\r
2769       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2770     y1 = boardRect.top +\r
2771       lineGap/2 + y * (squareSize + lineGap);\r
2772   } else {\r
2773     x1 = boardRect.left +\r
2774       lineGap/2 + x * (squareSize + lineGap);\r
2775     y1 = boardRect.top +\r
2776       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2777   }\r
2778   hPen = pen ? premovePen : highlightPen;\r
2779   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2780   MoveToEx(hdc, x1, y1, NULL);\r
2781   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2782   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2783   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2784   LineTo(hdc, x1, y1);\r
2785   SelectObject(hdc, oldPen);\r
2786 }\r
2787 \r
2788 VOID\r
2789 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2790 {\r
2791   int i;\r
2792   for (i=0; i<2; i++) {\r
2793     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2794       DrawHighlightOnDC(hdc, TRUE,\r
2795                         h->sq[i].x, h->sq[i].y,\r
2796                         pen);\r
2797   }\r
2798 }\r
2799 \r
2800 /* Note: sqcolor is used only in monoMode */\r
2801 /* Note that this code is largely duplicated in woptions.c,\r
2802    function DrawSampleSquare, so that needs to be updated too */\r
2803 VOID\r
2804 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2805 {\r
2806   HBITMAP oldBitmap;\r
2807   HBRUSH oldBrush;\r
2808   int tmpSize;\r
2809 \r
2810   if (appData.blindfold) return;\r
2811 \r
2812   /* [AS] Use font-based pieces if needed */\r
2813   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2814     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2815     CreatePiecesFromFont();\r
2816 \r
2817     if( fontBitmapSquareSize == squareSize ) {\r
2818         int index = TranslatePieceToFontPiece(piece);\r
2819 \r
2820         SelectObject( tmphdc, hPieceMask[ index ] );\r
2821 \r
2822       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2823         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2824       else\r
2825         BitBlt( hdc,\r
2826             x, y,\r
2827             squareSize, squareSize,\r
2828             tmphdc,\r
2829             0, 0,\r
2830             SRCAND );\r
2831 \r
2832         SelectObject( tmphdc, hPieceFace[ index ] );\r
2833 \r
2834       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2835         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2836       else\r
2837         BitBlt( hdc,\r
2838             x, y,\r
2839             squareSize, squareSize,\r
2840             tmphdc,\r
2841             0, 0,\r
2842             SRCPAINT );\r
2843 \r
2844         return;\r
2845     }\r
2846   }\r
2847 \r
2848   if (appData.monoMode) {\r
2849     SelectObject(tmphdc, PieceBitmap(piece, \r
2850       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2851     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2852            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2853   } else {\r
2854     tmpSize = squareSize;\r
2855     if(minorSize &&\r
2856         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2857          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2858       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2859       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2860       x += (squareSize - minorSize)>>1;\r
2861       y += squareSize - minorSize - 2;\r
2862       tmpSize = minorSize;\r
2863     }\r
2864     if (color || appData.allWhite ) {\r
2865       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2866       if( color )\r
2867               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2868       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2869       if(appData.upsideDown && color==flipView)\r
2870         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2871       else\r
2872         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2873       /* Use black for outline of white pieces */\r
2874       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2875       if(appData.upsideDown && color==flipView)\r
2876         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2877       else\r
2878         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2879     } else {\r
2880       /* Use square color for details of black pieces */\r
2881       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2882       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2883       if(appData.upsideDown && !flipView)\r
2884         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2885       else\r
2886         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2887     }\r
2888     SelectObject(hdc, oldBrush);\r
2889     SelectObject(tmphdc, oldBitmap);\r
2890   }\r
2891 }\r
2892 \r
2893 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2894 int GetBackTextureMode( int algo )\r
2895 {\r
2896     int result = BACK_TEXTURE_MODE_DISABLED;\r
2897 \r
2898     switch( algo ) \r
2899     {\r
2900         case BACK_TEXTURE_MODE_PLAIN:\r
2901             result = 1; /* Always use identity map */\r
2902             break;\r
2903         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2904             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2905             break;\r
2906     }\r
2907 \r
2908     return result;\r
2909 }\r
2910 \r
2911 /* \r
2912     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2913     to handle redraws cleanly (as random numbers would always be different).\r
2914 */\r
2915 VOID RebuildTextureSquareInfo()\r
2916 {\r
2917     BITMAP bi;\r
2918     int lite_w = 0;\r
2919     int lite_h = 0;\r
2920     int dark_w = 0;\r
2921     int dark_h = 0;\r
2922     int row;\r
2923     int col;\r
2924 \r
2925     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2926 \r
2927     if( liteBackTexture != NULL ) {\r
2928         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2929             lite_w = bi.bmWidth;\r
2930             lite_h = bi.bmHeight;\r
2931         }\r
2932     }\r
2933 \r
2934     if( darkBackTexture != NULL ) {\r
2935         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2936             dark_w = bi.bmWidth;\r
2937             dark_h = bi.bmHeight;\r
2938         }\r
2939     }\r
2940 \r
2941     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2942         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2943             if( (col + row) & 1 ) {\r
2944                 /* Lite square */\r
2945                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2946                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2947                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2948                   else\r
2949                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2950                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2951                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2952                   else\r
2953                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2954                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2955                 }\r
2956             }\r
2957             else {\r
2958                 /* Dark square */\r
2959                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2960                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2961                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2962                   else\r
2963                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2964                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2965                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2966                   else\r
2967                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2968                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2969                 }\r
2970             }\r
2971         }\r
2972     }\r
2973 }\r
2974 \r
2975 /* [AS] Arrow highlighting support */\r
2976 \r
2977 static double A_WIDTH = 5; /* Width of arrow body */\r
2978 \r
2979 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2980 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2981 \r
2982 static double Sqr( double x )\r
2983 {\r
2984     return x*x;\r
2985 }\r
2986 \r
2987 static int Round( double x )\r
2988 {\r
2989     return (int) (x + 0.5);\r
2990 }\r
2991 \r
2992 /* Draw an arrow between two points using current settings */\r
2993 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2994 {\r
2995     POINT arrow[7];\r
2996     double dx, dy, j, k, x, y;\r
2997 \r
2998     if( d_x == s_x ) {\r
2999         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3000 \r
3001         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3002         arrow[0].y = s_y;\r
3003 \r
3004         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3005         arrow[1].y = d_y - h;\r
3006 \r
3007         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3008         arrow[2].y = d_y - h;\r
3009 \r
3010         arrow[3].x = d_x;\r
3011         arrow[3].y = d_y;\r
3012 \r
3013         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3014         arrow[5].y = d_y - h;\r
3015 \r
3016         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3017         arrow[4].y = d_y - h;\r
3018 \r
3019         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3020         arrow[6].y = s_y;\r
3021     }\r
3022     else if( d_y == s_y ) {\r
3023         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3024 \r
3025         arrow[0].x = s_x;\r
3026         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3027 \r
3028         arrow[1].x = d_x - w;\r
3029         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3030 \r
3031         arrow[2].x = d_x - w;\r
3032         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3033 \r
3034         arrow[3].x = d_x;\r
3035         arrow[3].y = d_y;\r
3036 \r
3037         arrow[5].x = d_x - w;\r
3038         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3039 \r
3040         arrow[4].x = d_x - w;\r
3041         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3042 \r
3043         arrow[6].x = s_x;\r
3044         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3045     }\r
3046     else {\r
3047         /* [AS] Needed a lot of paper for this! :-) */\r
3048         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3049         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3050   \r
3051         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3052 \r
3053         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3054 \r
3055         x = s_x;\r
3056         y = s_y;\r
3057 \r
3058         arrow[0].x = Round(x - j);\r
3059         arrow[0].y = Round(y + j*dx);\r
3060 \r
3061         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3062         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3063 \r
3064         if( d_x > s_x ) {\r
3065             x = (double) d_x - k;\r
3066             y = (double) d_y - k*dy;\r
3067         }\r
3068         else {\r
3069             x = (double) d_x + k;\r
3070             y = (double) d_y + k*dy;\r
3071         }\r
3072 \r
3073         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3074 \r
3075         arrow[6].x = Round(x - j);\r
3076         arrow[6].y = Round(y + j*dx);\r
3077 \r
3078         arrow[2].x = Round(arrow[6].x + 2*j);\r
3079         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3080 \r
3081         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3082         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3083 \r
3084         arrow[4].x = d_x;\r
3085         arrow[4].y = d_y;\r
3086 \r
3087         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3088         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3089     }\r
3090 \r
3091     Polygon( hdc, arrow, 7 );\r
3092 }\r
3093 \r
3094 /* [AS] Draw an arrow between two squares */\r
3095 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3096 {\r
3097     int s_x, s_y, d_x, d_y;\r
3098     HPEN hpen;\r
3099     HPEN holdpen;\r
3100     HBRUSH hbrush;\r
3101     HBRUSH holdbrush;\r
3102     LOGBRUSH stLB;\r
3103 \r
3104     if( s_col == d_col && s_row == d_row ) {\r
3105         return;\r
3106     }\r
3107 \r
3108     /* Get source and destination points */\r
3109     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3110     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3111 \r
3112     if( d_y > s_y ) {\r
3113         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3114     }\r
3115     else if( d_y < s_y ) {\r
3116         d_y += squareSize / 2 + squareSize / 4;\r
3117     }\r
3118     else {\r
3119         d_y += squareSize / 2;\r
3120     }\r
3121 \r
3122     if( d_x > s_x ) {\r
3123         d_x += squareSize / 2 - squareSize / 4;\r
3124     }\r
3125     else if( d_x < s_x ) {\r
3126         d_x += squareSize / 2 + squareSize / 4;\r
3127     }\r
3128     else {\r
3129         d_x += squareSize / 2;\r
3130     }\r
3131 \r
3132     s_x += squareSize / 2;\r
3133     s_y += squareSize / 2;\r
3134 \r
3135     /* Adjust width */\r
3136     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3137 \r
3138     /* Draw */\r
3139     stLB.lbStyle = BS_SOLID;\r
3140     stLB.lbColor = appData.highlightArrowColor;\r
3141     stLB.lbHatch = 0;\r
3142 \r
3143     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3144     holdpen = SelectObject( hdc, hpen );\r
3145     hbrush = CreateBrushIndirect( &stLB );\r
3146     holdbrush = SelectObject( hdc, hbrush );\r
3147 \r
3148     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3149 \r
3150     SelectObject( hdc, holdpen );\r
3151     SelectObject( hdc, holdbrush );\r
3152     DeleteObject( hpen );\r
3153     DeleteObject( hbrush );\r
3154 }\r
3155 \r
3156 BOOL HasHighlightInfo()\r
3157 {\r
3158     BOOL result = FALSE;\r
3159 \r
3160     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3161         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3162     {\r
3163         result = TRUE;\r
3164     }\r
3165 \r
3166     return result;\r
3167 }\r
3168 \r
3169 BOOL IsDrawArrowEnabled()\r
3170 {\r
3171     BOOL result = FALSE;\r
3172 \r
3173     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3174         result = TRUE;\r
3175     }\r
3176 \r
3177     return result;\r
3178 }\r
3179 \r
3180 VOID DrawArrowHighlight( HDC hdc )\r
3181 {\r
3182     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3183         DrawArrowBetweenSquares( hdc,\r
3184             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3185             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3186     }\r
3187 }\r
3188 \r
3189 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3190 {\r
3191     HRGN result = NULL;\r
3192 \r
3193     if( HasHighlightInfo() ) {\r
3194         int x1, y1, x2, y2;\r
3195         int sx, sy, dx, dy;\r
3196 \r
3197         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3198         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3199 \r
3200         sx = MIN( x1, x2 );\r
3201         sy = MIN( y1, y2 );\r
3202         dx = MAX( x1, x2 ) + squareSize;\r
3203         dy = MAX( y1, y2 ) + squareSize;\r
3204 \r
3205         result = CreateRectRgn( sx, sy, dx, dy );\r
3206     }\r
3207 \r
3208     return result;\r
3209 }\r
3210 \r
3211 /*\r
3212     Warning: this function modifies the behavior of several other functions. \r
3213     \r
3214     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3215     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3216     repaint is scattered all over the place, which is not good for features such as\r
3217     "arrow highlighting" that require a full repaint of the board.\r
3218 \r
3219     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3220     user interaction, when speed is not so important) but especially to avoid errors\r
3221     in the displayed graphics.\r
3222 \r
3223     In such patched places, I always try refer to this function so there is a single\r
3224     place to maintain knowledge.\r
3225     \r
3226     To restore the original behavior, just return FALSE unconditionally.\r
3227 */\r
3228 BOOL IsFullRepaintPreferrable()\r
3229 {\r
3230     BOOL result = FALSE;\r
3231 \r
3232     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3233         /* Arrow may appear on the board */\r
3234         result = TRUE;\r
3235     }\r
3236 \r
3237     return result;\r
3238 }\r
3239 \r
3240 /* \r
3241     This function is called by DrawPosition to know whether a full repaint must\r
3242     be forced or not.\r
3243 \r
3244     Only DrawPosition may directly call this function, which makes use of \r
3245     some state information. Other function should call DrawPosition specifying \r
3246     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3247 */\r
3248 BOOL DrawPositionNeedsFullRepaint()\r
3249 {\r
3250     BOOL result = FALSE;\r
3251 \r
3252     /* \r
3253         Probably a slightly better policy would be to trigger a full repaint\r
3254         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3255         but animation is fast enough that it's difficult to notice.\r
3256     */\r
3257     if( animInfo.piece == EmptySquare ) {\r
3258         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3259             result = TRUE;\r
3260         }\r
3261     }\r
3262 \r
3263     return result;\r
3264 }\r
3265 \r
3266 VOID\r
3267 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3268 {\r
3269   int row, column, x, y, square_color, piece_color;\r
3270   ChessSquare piece;\r
3271   HBRUSH oldBrush;\r
3272   HDC texture_hdc = NULL;\r
3273 \r
3274   /* [AS] Initialize background textures if needed */\r
3275   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3276       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3277       if( backTextureSquareSize != squareSize \r
3278        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3279           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3280           backTextureSquareSize = squareSize;\r
3281           RebuildTextureSquareInfo();\r
3282       }\r
3283 \r
3284       texture_hdc = CreateCompatibleDC( hdc );\r
3285   }\r
3286 \r
3287   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3288     for (column = 0; column < BOARD_WIDTH; column++) {\r
3289   \r
3290       SquareToPos(row, column, &x, &y);\r
3291 \r
3292       piece = board[row][column];\r
3293 \r
3294       square_color = ((column + row) % 2) == 1;\r
3295       if( gameInfo.variant == VariantXiangqi ) {\r
3296           square_color = !InPalace(row, column);\r
3297           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3298           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3299       }\r
3300       piece_color = (int) piece < (int) BlackPawn;\r
3301 \r
3302 \r
3303       /* [HGM] holdings file: light square or black */\r
3304       if(column == BOARD_LEFT-2) {\r
3305             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3306                 square_color = 1;\r
3307             else {\r
3308                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3309                 continue;\r
3310             }\r
3311       } else\r
3312       if(column == BOARD_RGHT + 1 ) {\r
3313             if( row < gameInfo.holdingsSize )\r
3314                 square_color = 1;\r
3315             else {\r
3316                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3317                 continue;\r
3318             }\r
3319       }\r
3320       if(column == BOARD_LEFT-1 ) /* left align */\r
3321             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3322       else if( column == BOARD_RGHT) /* right align */\r
3323             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3324       else\r
3325       if (appData.monoMode) {\r
3326         if (piece == EmptySquare) {\r
3327           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3328                  square_color ? WHITENESS : BLACKNESS);\r
3329         } else {\r
3330           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3331         }\r
3332       } \r
3333       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3334           /* [AS] Draw the square using a texture bitmap */\r
3335           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3336           int r = row, c = column; // [HGM] do not flip board in flipView\r
3337           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3338 \r
3339           DrawTile( x, y, \r
3340               squareSize, squareSize, \r
3341               hdc, \r
3342               texture_hdc,\r
3343               backTextureSquareInfo[r][c].mode,\r
3344               backTextureSquareInfo[r][c].x,\r
3345               backTextureSquareInfo[r][c].y );\r
3346 \r
3347           SelectObject( texture_hdc, hbm );\r
3348 \r
3349           if (piece != EmptySquare) {\r
3350               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3351           }\r
3352       }\r
3353       else {\r
3354         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3355 \r
3356         oldBrush = SelectObject(hdc, brush );\r
3357         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3358         SelectObject(hdc, oldBrush);\r
3359         if (piece != EmptySquare)\r
3360           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3361       }\r
3362     }\r
3363   }\r
3364 \r
3365   if( texture_hdc != NULL ) {\r
3366     DeleteDC( texture_hdc );\r
3367   }\r
3368 }\r
3369 \r
3370 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3371 void fputDW(FILE *f, int x)\r
3372 {\r
3373         fputc(x     & 255, f);\r
3374         fputc(x>>8  & 255, f);\r
3375         fputc(x>>16 & 255, f);\r
3376         fputc(x>>24 & 255, f);\r
3377 }\r
3378 \r
3379 #define MAX_CLIPS 200   /* more than enough */\r
3380 \r
3381 VOID\r
3382 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3383 {\r
3384 //  HBITMAP bufferBitmap;\r
3385   BITMAP bi;\r
3386 //  RECT Rect;\r
3387   HDC tmphdc;\r
3388   HBITMAP hbm;\r
3389   int w = 100, h = 50;\r
3390 \r
3391   if(logo == NULL) return;\r
3392 //  GetClientRect(hwndMain, &Rect);\r
3393 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3394 //                                      Rect.bottom-Rect.top+1);\r
3395   tmphdc = CreateCompatibleDC(hdc);\r
3396   hbm = SelectObject(tmphdc, logo);\r
3397   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3398             w = bi.bmWidth;\r
3399             h = bi.bmHeight;\r
3400   }\r
3401   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3402                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3403   SelectObject(tmphdc, hbm);\r
3404   DeleteDC(tmphdc);\r
3405 }\r
3406 \r
3407 VOID\r
3408 DisplayLogos()\r
3409 {\r
3410   if(logoHeight) {\r
3411         HDC hdc = GetDC(hwndMain);\r
3412         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3413         if(appData.autoLogo) {\r
3414           \r
3415           switch(gameMode) { // pick logos based on game mode\r
3416             case IcsObserving:\r
3417                 whiteLogo = second.programLogo; // ICS logo\r
3418                 blackLogo = second.programLogo;\r
3419             default:\r
3420                 break;\r
3421             case IcsPlayingWhite:\r
3422                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3423                 blackLogo = second.programLogo; // ICS logo\r
3424                 break;\r
3425             case IcsPlayingBlack:\r
3426                 whiteLogo = second.programLogo; // ICS logo\r
3427                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3428                 break;\r
3429             case TwoMachinesPlay:\r
3430                 if(first.twoMachinesColor[0] == 'b') {\r
3431                     whiteLogo = second.programLogo;\r
3432                     blackLogo = first.programLogo;\r
3433                 }\r
3434                 break;\r
3435             case MachinePlaysWhite:\r
3436                 blackLogo = userLogo;\r
3437                 break;\r
3438             case MachinePlaysBlack:\r
3439                 whiteLogo = userLogo;\r
3440                 blackLogo = first.programLogo;\r
3441           }\r
3442         }\r
3443         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3444         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3445         ReleaseDC(hwndMain, hdc);\r
3446   }\r
3447 }\r
3448 \r
3449 static HDC hdcSeek;\r
3450 \r
3451 // [HGM] seekgraph\r
3452 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3453 {\r
3454     POINT stPt;\r
3455     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3456     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3457     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3458     SelectObject( hdcSeek, hp );\r
3459 }\r
3460 \r
3461 // front-end wrapper for drawing functions to do rectangles\r
3462 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3463 {\r
3464     HPEN hp;\r
3465     RECT rc;\r
3466 \r
3467     if (hdcSeek == NULL) {\r
3468     hdcSeek = GetDC(hwndMain);\r
3469       if (!appData.monoMode) {\r
3470         SelectPalette(hdcSeek, hPal, FALSE);\r
3471         RealizePalette(hdcSeek);\r
3472       }\r
3473     }\r
3474     hp = SelectObject( hdcSeek, gridPen );\r
3475     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3476     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3477     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3478     SelectObject( hdcSeek, hp );\r
3479 }\r
3480 \r
3481 // front-end wrapper for putting text in graph\r
3482 void DrawSeekText(char *buf, int x, int y)\r
3483 {\r
3484         SIZE stSize;\r
3485         SetBkMode( hdcSeek, TRANSPARENT );\r
3486         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3487         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3488 }\r
3489 \r
3490 void DrawSeekDot(int x, int y, int color)\r
3491 {\r
3492         int square = color & 0x80;\r
3493         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3494                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3495         color &= 0x7F;\r
3496         if(square)\r
3497             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3498                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3499         else\r
3500             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3501                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3502             SelectObject(hdcSeek, oldBrush);\r
3503 }\r
3504 \r
3505 VOID\r
3506 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3507 {\r
3508   static Board lastReq[2], lastDrawn[2];\r
3509   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3510   static int lastDrawnFlipView = 0;\r
3511   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3512   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3513   HDC tmphdc;\r
3514   HDC hdcmem;\r
3515   HBITMAP bufferBitmap;\r
3516   HBITMAP oldBitmap;\r
3517   RECT Rect;\r
3518   HRGN clips[MAX_CLIPS];\r
3519   ChessSquare dragged_piece = EmptySquare;\r
3520   int nr = twoBoards*partnerUp;\r
3521 \r
3522   /* I'm undecided on this - this function figures out whether a full\r
3523    * repaint is necessary on its own, so there's no real reason to have the\r
3524    * caller tell it that.  I think this can safely be set to FALSE - but\r
3525    * if we trust the callers not to request full repaints unnessesarily, then\r
3526    * we could skip some clipping work.  In other words, only request a full\r
3527    * redraw when the majority of pieces have changed positions (ie. flip, \r
3528    * gamestart and similar)  --Hawk\r
3529    */\r
3530   Boolean fullrepaint = repaint;\r
3531 \r
3532   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3533 \r
3534   if( DrawPositionNeedsFullRepaint() ) {\r
3535       fullrepaint = TRUE;\r
3536   }\r
3537 \r
3538   if (board == NULL) {\r
3539     if (!lastReqValid[nr]) {\r
3540       return;\r
3541     }\r
3542     board = lastReq[nr];\r
3543   } else {\r
3544     CopyBoard(lastReq[nr], board);\r
3545     lastReqValid[nr] = 1;\r
3546   }\r
3547 \r
3548   if (doingSizing) {\r
3549     return;\r
3550   }\r
3551 \r
3552   if (IsIconic(hwndMain)) {\r
3553     return;\r
3554   }\r
3555 \r
3556   if (hdc == NULL) {\r
3557     hdc = GetDC(hwndMain);\r
3558     if (!appData.monoMode) {\r
3559       SelectPalette(hdc, hPal, FALSE);\r
3560       RealizePalette(hdc);\r
3561     }\r
3562     releaseDC = TRUE;\r
3563   } else {\r
3564     releaseDC = FALSE;\r
3565   }\r
3566 \r
3567   /* Create some work-DCs */\r
3568   hdcmem = CreateCompatibleDC(hdc);\r
3569   tmphdc = CreateCompatibleDC(hdc);\r
3570 \r
3571   /* If dragging is in progress, we temporarely remove the piece */\r
3572   /* [HGM] or temporarily decrease count if stacked              */\r
3573   /*       !! Moved to before board compare !!                   */\r
3574   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3575     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3576     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3577             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3578         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3579     } else \r
3580     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3581             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3582         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3583     } else \r
3584         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3585   }\r
3586 \r
3587   /* Figure out which squares need updating by comparing the \r
3588    * newest board with the last drawn board and checking if\r
3589    * flipping has changed.\r
3590    */\r
3591   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3592     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3593       for (column = 0; column < BOARD_WIDTH; column++) {\r
3594         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3595           SquareToPos(row, column, &x, &y);\r
3596           clips[num_clips++] =\r
3597             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3598         }\r
3599       }\r
3600     }\r
3601    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3602     for (i=0; i<2; i++) {\r
3603       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3604           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3605         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3606             lastDrawnHighlight.sq[i].y >= 0) {\r
3607           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3608                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3609           clips[num_clips++] =\r
3610             CreateRectRgn(x - lineGap, y - lineGap, \r
3611                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3612         }\r
3613         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3614           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3615           clips[num_clips++] =\r
3616             CreateRectRgn(x - lineGap, y - lineGap, \r
3617                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3618         }\r
3619       }\r
3620     }\r
3621     for (i=0; i<2; i++) {\r
3622       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3623           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3624         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3625             lastDrawnPremove.sq[i].y >= 0) {\r
3626           SquareToPos(lastDrawnPremove.sq[i].y,\r
3627                       lastDrawnPremove.sq[i].x, &x, &y);\r
3628           clips[num_clips++] =\r
3629             CreateRectRgn(x - lineGap, y - lineGap, \r
3630                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3631         }\r
3632         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3633             premoveHighlightInfo.sq[i].y >= 0) {\r
3634           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3635                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3636           clips[num_clips++] =\r
3637             CreateRectRgn(x - lineGap, y - lineGap, \r
3638                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3639         }\r
3640       }\r
3641     }\r
3642    } else { // nr == 1\r
3643         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3644         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3645         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3646         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3647       for (i=0; i<2; i++) {\r
3648         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3649             partnerHighlightInfo.sq[i].y >= 0) {\r
3650           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3651                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3652           clips[num_clips++] =\r
3653             CreateRectRgn(x - lineGap, y - lineGap, \r
3654                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3655         }\r
3656         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3657             oldPartnerHighlight.sq[i].y >= 0) {\r
3658           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3659                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3660           clips[num_clips++] =\r
3661             CreateRectRgn(x - lineGap, y - lineGap, \r
3662                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3663         }\r
3664       }\r
3665    }\r
3666   } else {\r
3667     fullrepaint = TRUE;\r
3668   }\r
3669 \r
3670   /* Create a buffer bitmap - this is the actual bitmap\r
3671    * being written to.  When all the work is done, we can\r
3672    * copy it to the real DC (the screen).  This avoids\r
3673    * the problems with flickering.\r
3674    */\r
3675   GetClientRect(hwndMain, &Rect);\r
3676   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3677                                         Rect.bottom-Rect.top+1);\r
3678   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3679   if (!appData.monoMode) {\r
3680     SelectPalette(hdcmem, hPal, FALSE);\r
3681   }\r
3682 \r
3683   /* Create clips for dragging */\r
3684   if (!fullrepaint) {\r
3685     if (dragInfo.from.x >= 0) {\r
3686       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3687       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3688     }\r
3689     if (dragInfo.start.x >= 0) {\r
3690       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3691       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3692     }\r
3693     if (dragInfo.pos.x >= 0) {\r
3694       x = dragInfo.pos.x - squareSize / 2;\r
3695       y = dragInfo.pos.y - squareSize / 2;\r
3696       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3697     }\r
3698     if (dragInfo.lastpos.x >= 0) {\r
3699       x = dragInfo.lastpos.x - squareSize / 2;\r
3700       y = dragInfo.lastpos.y - squareSize / 2;\r
3701       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3702     }\r
3703   }\r
3704 \r
3705   /* Are we animating a move?  \r
3706    * If so, \r
3707    *   - remove the piece from the board (temporarely)\r
3708    *   - calculate the clipping region\r
3709    */\r
3710   if (!fullrepaint) {\r
3711     if (animInfo.piece != EmptySquare) {\r
3712       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3713       x = boardRect.left + animInfo.lastpos.x;\r
3714       y = boardRect.top + animInfo.lastpos.y;\r
3715       x2 = boardRect.left + animInfo.pos.x;\r
3716       y2 = boardRect.top + animInfo.pos.y;\r
3717       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3718       /* Slight kludge.  The real problem is that after AnimateMove is\r
3719          done, the position on the screen does not match lastDrawn.\r
3720          This currently causes trouble only on e.p. captures in\r
3721          atomic, where the piece moves to an empty square and then\r
3722          explodes.  The old and new positions both had an empty square\r
3723          at the destination, but animation has drawn a piece there and\r
3724          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3725       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3726     }\r
3727   }\r
3728 \r
3729   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3730   if (num_clips == 0)\r
3731     fullrepaint = TRUE;\r
3732 \r
3733   /* Set clipping on the memory DC */\r
3734   if (!fullrepaint) {\r
3735     SelectClipRgn(hdcmem, clips[0]);\r
3736     for (x = 1; x < num_clips; x++) {\r
3737       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3738         abort();  // this should never ever happen!\r
3739     }\r
3740   }\r
3741 \r
3742   /* Do all the drawing to the memory DC */\r
3743   if(explodeInfo.radius) { // [HGM] atomic\r
3744         HBRUSH oldBrush;\r
3745         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3746         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3747         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3748         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3749         x += squareSize/2;\r
3750         y += squareSize/2;\r
3751         if(!fullrepaint) {\r
3752           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3753           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3754         }\r
3755         DrawGridOnDC(hdcmem);\r
3756         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3757         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3758         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3759         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3760         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3761         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3762         SelectObject(hdcmem, oldBrush);\r
3763   } else {\r
3764     DrawGridOnDC(hdcmem);\r
3765     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3766         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3767         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3768     } else {\r
3769         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3770         oldPartnerHighlight = partnerHighlightInfo;\r
3771     }\r
3772     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3773   }\r
3774   if(nr == 0) // [HGM] dual: markers only on left board\r
3775   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3776     for (column = 0; column < BOARD_WIDTH; column++) {\r
3777         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3778             HBRUSH oldBrush = SelectObject(hdcmem, \r
3779                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3780             SquareToPos(row, column, &x, &y);\r
3781             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3782                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3783             SelectObject(hdcmem, oldBrush);\r
3784         }\r
3785     }\r
3786   }\r
3787 \r
3788   if( appData.highlightMoveWithArrow ) {\r
3789     DrawArrowHighlight(hdcmem);\r
3790   }\r
3791 \r
3792   DrawCoordsOnDC(hdcmem);\r
3793 \r
3794   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3795                  /* to make sure lastDrawn contains what is actually drawn */\r
3796 \r
3797   /* Put the dragged piece back into place and draw it (out of place!) */\r
3798     if (dragged_piece != EmptySquare) {\r
3799     /* [HGM] or restack */\r
3800     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3801                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3802     else\r
3803     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3804                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3805     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3806     x = dragInfo.pos.x - squareSize / 2;\r
3807     y = dragInfo.pos.y - squareSize / 2;\r
3808     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3809                   ((int) dragInfo.piece < (int) BlackPawn), \r
3810                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3811   }   \r
3812   \r
3813   /* Put the animated piece back into place and draw it */\r
3814   if (animInfo.piece != EmptySquare) {\r
3815     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3816     x = boardRect.left + animInfo.pos.x;\r
3817     y = boardRect.top + animInfo.pos.y;\r
3818     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3819                   ((int) animInfo.piece < (int) BlackPawn),\r
3820                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3821   }\r
3822 \r
3823   /* Release the bufferBitmap by selecting in the old bitmap \r
3824    * and delete the memory DC\r
3825    */\r
3826   SelectObject(hdcmem, oldBitmap);\r
3827   DeleteDC(hdcmem);\r
3828 \r
3829   /* Set clipping on the target DC */\r
3830   if (!fullrepaint) {\r
3831     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3832         RECT rect;\r
3833         GetRgnBox(clips[x], &rect);\r
3834         DeleteObject(clips[x]);\r
3835         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3836                           rect.right + wpMain.width/2, rect.bottom);\r
3837     }\r
3838     SelectClipRgn(hdc, clips[0]);\r
3839     for (x = 1; x < num_clips; x++) {\r
3840       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3841         abort();   // this should never ever happen!\r
3842     } \r
3843   }\r
3844 \r
3845   /* Copy the new bitmap onto the screen in one go.\r
3846    * This way we avoid any flickering\r
3847    */\r
3848   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3849   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3850          boardRect.right - boardRect.left,\r
3851          boardRect.bottom - boardRect.top,\r
3852          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3853   if(saveDiagFlag) { \r
3854     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3855     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3856 \r
3857     GetObject(bufferBitmap, sizeof(b), &b);\r
3858     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3859         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3860         bih.biWidth = b.bmWidth;\r
3861         bih.biHeight = b.bmHeight;\r
3862         bih.biPlanes = 1;\r
3863         bih.biBitCount = b.bmBitsPixel;\r
3864         bih.biCompression = 0;\r
3865         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3866         bih.biXPelsPerMeter = 0;\r
3867         bih.biYPelsPerMeter = 0;\r
3868         bih.biClrUsed = 0;\r
3869         bih.biClrImportant = 0;\r
3870 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3871 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3872         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3873 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3874 \r
3875         wb = b.bmWidthBytes;\r
3876         // count colors\r
3877         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3878                 int k = ((int*) pData)[i];\r
3879                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3880                 if(j >= 16) break;\r
3881                 color[j] = k;\r
3882                 if(j >= nrColors) nrColors = j+1;\r
3883         }\r
3884         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3885                 INT p = 0;\r
3886                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3887                     for(w=0; w<(wb>>2); w+=2) {\r
3888                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3889                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3890                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3891                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3892                         pData[p++] = m | j<<4;\r
3893                     }\r
3894                     while(p&3) pData[p++] = 0;\r
3895                 }\r
3896                 fac = 3;\r
3897                 wb = ((wb+31)>>5)<<2;\r
3898         }\r
3899         // write BITMAPFILEHEADER\r
3900         fprintf(diagFile, "BM");\r
3901         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3902         fputDW(diagFile, 0);\r
3903         fputDW(diagFile, 0x36 + (fac?64:0));\r
3904         // write BITMAPINFOHEADER\r
3905         fputDW(diagFile, 40);\r
3906         fputDW(diagFile, b.bmWidth);\r
3907         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3908         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3909         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3910         fputDW(diagFile, 0);\r
3911         fputDW(diagFile, 0);\r
3912         fputDW(diagFile, 0);\r
3913         fputDW(diagFile, 0);\r
3914         fputDW(diagFile, 0);\r
3915         fputDW(diagFile, 0);\r
3916         // write color table\r
3917         if(fac)\r
3918         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3919         // write bitmap data\r
3920         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3921                 fputc(pData[i], diagFile);\r
3922         free(pData);\r
3923      }\r
3924   }\r
3925 \r
3926   SelectObject(tmphdc, oldBitmap);\r
3927 \r
3928   /* Massive cleanup */\r
3929   for (x = 0; x < num_clips; x++)\r
3930     DeleteObject(clips[x]);\r
3931 \r
3932   DeleteDC(tmphdc);\r
3933   DeleteObject(bufferBitmap);\r
3934 \r
3935   if (releaseDC) \r
3936     ReleaseDC(hwndMain, hdc);\r
3937   \r
3938   if (lastDrawnFlipView != flipView && nr == 0) {\r
3939     if (flipView)\r
3940       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3941     else\r
3942       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3943   }\r
3944 \r
3945 /*  CopyBoard(lastDrawn, board);*/\r
3946   lastDrawnHighlight = highlightInfo;\r
3947   lastDrawnPremove   = premoveHighlightInfo;\r
3948   lastDrawnFlipView = flipView;\r
3949   lastDrawnValid[nr] = 1;\r
3950 }\r
3951 \r
3952 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3953 int\r
3954 SaveDiagram(f)\r
3955      FILE *f;\r
3956 {\r
3957     saveDiagFlag = 1; diagFile = f;\r
3958     HDCDrawPosition(NULL, TRUE, NULL);\r
3959     saveDiagFlag = 0;\r
3960 \r
3961     fclose(f);\r
3962     return TRUE;\r
3963 }\r
3964 \r
3965 \r
3966 /*---------------------------------------------------------------------------*\\r
3967 | CLIENT PAINT PROCEDURE\r
3968 |   This is the main event-handler for the WM_PAINT message.\r
3969 |\r
3970 \*---------------------------------------------------------------------------*/\r
3971 VOID\r
3972 PaintProc(HWND hwnd)\r
3973 {\r
3974   HDC         hdc;\r
3975   PAINTSTRUCT ps;\r
3976   HFONT       oldFont;\r
3977 \r
3978   if((hdc = BeginPaint(hwnd, &ps))) {\r
3979     if (IsIconic(hwnd)) {\r
3980       DrawIcon(hdc, 2, 2, iconCurrent);\r
3981     } else {\r
3982       if (!appData.monoMode) {\r
3983         SelectPalette(hdc, hPal, FALSE);\r
3984         RealizePalette(hdc);\r
3985       }\r
3986       HDCDrawPosition(hdc, 1, NULL);\r
3987       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3988         flipView = !flipView; partnerUp = !partnerUp;\r
3989         HDCDrawPosition(hdc, 1, NULL);\r
3990         flipView = !flipView; partnerUp = !partnerUp;\r
3991       }\r
3992       oldFont =\r
3993         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3994       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3995                  ETO_CLIPPED|ETO_OPAQUE,\r
3996                  &messageRect, messageText, strlen(messageText), NULL);\r
3997       SelectObject(hdc, oldFont);\r
3998       DisplayBothClocks();\r
3999       DisplayLogos();\r
4000     }\r
4001     EndPaint(hwnd,&ps);\r
4002   }\r
4003 \r
4004   return;\r
4005 }\r
4006 \r
4007 \r
4008 /*\r
4009  * If the user selects on a border boundary, return -1; if off the board,\r
4010  *   return -2.  Otherwise map the event coordinate to the square.\r
4011  * The offset boardRect.left or boardRect.top must already have been\r
4012  *   subtracted from x.\r
4013  */\r
4014 int EventToSquare(x, limit)\r
4015      int x, limit;\r
4016 {\r
4017   if (x <= 0)\r
4018     return -2;\r
4019   if (x < lineGap)\r
4020     return -1;\r
4021   x -= lineGap;\r
4022   if ((x % (squareSize + lineGap)) >= squareSize)\r
4023     return -1;\r
4024   x /= (squareSize + lineGap);\r
4025     if (x >= limit)\r
4026     return -2;\r
4027   return x;\r
4028 }\r
4029 \r
4030 typedef struct {\r
4031   char piece;\r
4032   int command;\r
4033   char* name;\r
4034 } DropEnable;\r
4035 \r
4036 DropEnable dropEnables[] = {\r
4037   { 'P', DP_Pawn, N_("Pawn") },\r
4038   { 'N', DP_Knight, N_("Knight") },\r
4039   { 'B', DP_Bishop, N_("Bishop") },\r
4040   { 'R', DP_Rook, N_("Rook") },\r
4041   { 'Q', DP_Queen, N_("Queen") },\r
4042 };\r
4043 \r
4044 VOID\r
4045 SetupDropMenu(HMENU hmenu)\r
4046 {\r
4047   int i, count, enable;\r
4048   char *p;\r
4049   extern char white_holding[], black_holding[];\r
4050   char item[MSG_SIZ];\r
4051 \r
4052   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4053     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4054                dropEnables[i].piece);\r
4055     count = 0;\r
4056     while (p && *p++ == dropEnables[i].piece) count++;\r
4057       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4058     enable = count > 0 || !appData.testLegality\r
4059       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4060                       && !appData.icsActive);\r
4061     ModifyMenu(hmenu, dropEnables[i].command,\r
4062                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4063                dropEnables[i].command, item);\r
4064   }\r
4065 }\r
4066 \r
4067 void DragPieceBegin(int x, int y)\r
4068 {\r
4069       dragInfo.lastpos.x = boardRect.left + x;\r
4070       dragInfo.lastpos.y = boardRect.top + y;\r
4071       dragInfo.from.x = fromX;\r
4072       dragInfo.from.y = fromY;\r
4073       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4074       dragInfo.start = dragInfo.from;\r
4075       SetCapture(hwndMain);\r
4076 }\r
4077 \r
4078 void DragPieceEnd(int x, int y)\r
4079 {\r
4080     ReleaseCapture();\r
4081     dragInfo.start.x = dragInfo.start.y = -1;\r
4082     dragInfo.from = dragInfo.start;\r
4083     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4084 }\r
4085 \r
4086 void ChangeDragPiece(ChessSquare piece)\r
4087 {\r
4088     dragInfo.piece = piece;\r
4089 }\r
4090 \r
4091 /* Event handler for mouse messages */\r
4092 VOID\r
4093 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4094 {\r
4095   int x, y, menuNr;\r
4096   POINT pt;\r
4097   static int recursive = 0;\r
4098   HMENU hmenu;\r
4099   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4100 \r
4101   if (recursive) {\r
4102     if (message == WM_MBUTTONUP) {\r
4103       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4104          to the middle button: we simulate pressing the left button too!\r
4105          */\r
4106       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4107       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4108     }\r
4109     return;\r
4110   }\r
4111   recursive++;\r
4112   \r
4113   pt.x = LOWORD(lParam);\r
4114   pt.y = HIWORD(lParam);\r
4115   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4116   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4117   if (!flipView && y >= 0) {\r
4118     y = BOARD_HEIGHT - 1 - y;\r
4119   }\r
4120   if (flipView && x >= 0) {\r
4121     x = BOARD_WIDTH - 1 - x;\r
4122   }\r
4123 \r
4124   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4125 \r
4126   switch (message) {\r
4127   case WM_LBUTTONDOWN:\r
4128       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4129         ClockClick(flipClock);\r
4130       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4131         ClockClick(!flipClock);\r
4132       }\r
4133       dragInfo.start.x = dragInfo.start.y = -1;\r
4134       dragInfo.from = dragInfo.start;\r
4135     if(fromX == -1 && frozen) { // not sure where this is for\r
4136                 fromX = fromY = -1; \r
4137       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4138       break;\r
4139     }\r
4140       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4141       DrawPosition(TRUE, NULL);\r
4142     break;\r
4143 \r
4144   case WM_LBUTTONUP:\r
4145       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4146       DrawPosition(TRUE, NULL);\r
4147     break;\r
4148 \r
4149   case WM_MOUSEMOVE:\r
4150     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4151     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4152     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4153     if ((appData.animateDragging || appData.highlightDragging)\r
4154         && (wParam & MK_LBUTTON)\r
4155         && dragInfo.from.x >= 0) \r
4156     {\r
4157       BOOL full_repaint = FALSE;\r
4158 \r
4159       if (appData.animateDragging) {\r
4160         dragInfo.pos = pt;\r
4161       }\r
4162       if (appData.highlightDragging) {\r
4163         SetHighlights(fromX, fromY, x, y);\r
4164         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4165             full_repaint = TRUE;\r
4166         }\r
4167       }\r
4168       \r
4169       DrawPosition( full_repaint, NULL);\r
4170       \r
4171       dragInfo.lastpos = dragInfo.pos;\r
4172     }\r
4173     break;\r
4174 \r
4175   case WM_MOUSEWHEEL: // [DM]\r
4176     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4177        /* Mouse Wheel is being rolled forward\r
4178         * Play moves forward\r
4179         */\r
4180        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4181                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4182        /* Mouse Wheel is being rolled backward\r
4183         * Play moves backward\r
4184         */\r
4185        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4186                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4187     }\r
4188     break;\r
4189 \r
4190   case WM_MBUTTONUP:\r
4191   case WM_RBUTTONUP:\r
4192     ReleaseCapture();\r
4193     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4194     break;\r
4195  \r
4196   case WM_MBUTTONDOWN:\r
4197   case WM_RBUTTONDOWN:\r
4198     ErrorPopDown();\r
4199     ReleaseCapture();\r
4200     fromX = fromY = -1;\r
4201     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4202     dragInfo.start.x = dragInfo.start.y = -1;\r
4203     dragInfo.from = dragInfo.start;\r
4204     dragInfo.lastpos = dragInfo.pos;\r
4205     if (appData.highlightDragging) {\r
4206       ClearHighlights();\r
4207     }\r
4208     if(y == -2) {\r
4209       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4210       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4211           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4212       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4213           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4214       }\r
4215       break;\r
4216     }\r
4217     DrawPosition(TRUE, NULL);\r
4218 \r
4219     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4220     switch (menuNr) {\r
4221     case 0:\r
4222       if (message == WM_MBUTTONDOWN) {\r
4223         buttonCount = 3;  /* even if system didn't think so */\r
4224         if (wParam & MK_SHIFT) \r
4225           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4226         else\r
4227           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4228       } else { /* message == WM_RBUTTONDOWN */\r
4229         /* Just have one menu, on the right button.  Windows users don't\r
4230            think to try the middle one, and sometimes other software steals\r
4231            it, or it doesn't really exist. */\r
4232         if(gameInfo.variant != VariantShogi)\r
4233             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4234         else\r
4235             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4236       }\r
4237       break;\r
4238     case 2:\r
4239       SetCapture(hwndMain);
4240       break;\r
4241     case 1:\r
4242       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4243       SetupDropMenu(hmenu);\r
4244       MenuPopup(hwnd, pt, hmenu, -1);\r
4245     default:\r
4246       break;\r
4247     }\r
4248     break;\r
4249   }\r
4250 \r
4251   recursive--;\r
4252 }\r
4253 \r
4254 /* Preprocess messages for buttons in main window */\r
4255 LRESULT CALLBACK\r
4256 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4257 {\r
4258   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4259   int i, dir;\r
4260 \r
4261   for (i=0; i<N_BUTTONS; i++) {\r
4262     if (buttonDesc[i].id == id) break;\r
4263   }\r
4264   if (i == N_BUTTONS) return 0;\r
4265   switch (message) {\r
4266   case WM_KEYDOWN:\r
4267     switch (wParam) {\r
4268     case VK_LEFT:\r
4269     case VK_RIGHT:\r
4270       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4271       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4272       return TRUE;\r
4273     }\r
4274     break;\r
4275   case WM_CHAR:\r
4276     switch (wParam) {\r
4277     case '\r':\r
4278       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4279       return TRUE;\r
4280     default:\r
4281       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4282         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4283         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4284         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4285         SetFocus(h);\r
4286         SendMessage(h, WM_CHAR, wParam, lParam);\r
4287         return TRUE;\r
4288       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4289         TypeInEvent((char)wParam);\r
4290       }\r
4291       break;\r
4292     }\r
4293     break;\r
4294   }\r
4295   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4296 }\r
4297 \r
4298 /* Process messages for Promotion dialog box */\r
4299 LRESULT CALLBACK\r
4300 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4301 {\r
4302   char promoChar;\r
4303 \r
4304   switch (message) {\r
4305   case WM_INITDIALOG: /* message: initialize dialog box */\r
4306     /* Center the dialog over the application window */\r
4307     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4308     Translate(hDlg, DLG_PromotionKing);\r
4309     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4310       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4311        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4312        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4313                SW_SHOW : SW_HIDE);\r
4314     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4315     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4316        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4317          PieceToChar(WhiteAngel) != '~') ||\r
4318         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4319          PieceToChar(BlackAngel) != '~')   ) ?\r
4320                SW_SHOW : SW_HIDE);\r
4321     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4322        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4323          PieceToChar(WhiteMarshall) != '~') ||\r
4324         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4325          PieceToChar(BlackMarshall) != '~')   ) ?\r
4326                SW_SHOW : SW_HIDE);\r
4327     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4328     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4329        gameInfo.variant != VariantShogi ?\r
4330                SW_SHOW : SW_HIDE);\r
4331     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4332        gameInfo.variant != VariantShogi ?\r
4333                SW_SHOW : SW_HIDE);\r
4334     if(gameInfo.variant == VariantShogi) {\r
4335         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4336         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4337         SetWindowText(hDlg, "Promote?");\r
4338     }\r
4339     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4340        gameInfo.variant == VariantSuper ?\r
4341                SW_SHOW : SW_HIDE);\r
4342     return TRUE;\r
4343 \r
4344   case WM_COMMAND: /* message: received a command */\r
4345     switch (LOWORD(wParam)) {\r
4346     case IDCANCEL:\r
4347       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4348       ClearHighlights();\r
4349       DrawPosition(FALSE, NULL);\r
4350       return TRUE;\r
4351     case PB_King:\r
4352       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4353       break;\r
4354     case PB_Queen:\r
4355       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4356       break;\r
4357     case PB_Rook:\r
4358       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4359       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4360       break;\r
4361     case PB_Bishop:\r
4362       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4363       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4364       break;\r
4365     case PB_Chancellor:\r
4366       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4367       break;\r
4368     case PB_Archbishop:\r
4369       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4370       break;\r
4371     case PB_Knight:\r
4372       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4373       break;\r
4374     default:\r
4375       return FALSE;\r
4376     }\r
4377     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4378     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4379     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4380     fromX = fromY = -1;\r
4381     if (!appData.highlightLastMove) {\r
4382       ClearHighlights();\r
4383       DrawPosition(FALSE, NULL);\r
4384     }\r
4385     return TRUE;\r
4386   }\r
4387   return FALSE;\r
4388 }\r
4389 \r
4390 /* Pop up promotion dialog */\r
4391 VOID\r
4392 PromotionPopup(HWND hwnd)\r
4393 {\r
4394   FARPROC lpProc;\r
4395 \r
4396   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4397   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4398     hwnd, (DLGPROC)lpProc);\r
4399   FreeProcInstance(lpProc);\r
4400 }\r
4401 \r
4402 void\r
4403 PromotionPopUp()\r
4404 {\r
4405   DrawPosition(TRUE, NULL);\r
4406   PromotionPopup(hwndMain);\r
4407 }\r
4408 \r
4409 /* Toggle ShowThinking */\r
4410 VOID\r
4411 ToggleShowThinking()\r
4412 {\r
4413   appData.showThinking = !appData.showThinking;\r
4414   ShowThinkingEvent();\r
4415 }\r
4416 \r
4417 VOID\r
4418 LoadGameDialog(HWND hwnd, char* title)\r
4419 {\r
4420   UINT number = 0;\r
4421   FILE *f;\r
4422   char fileTitle[MSG_SIZ];\r
4423   f = OpenFileDialog(hwnd, "rb", "",\r
4424                      appData.oldSaveStyle ? "gam" : "pgn",\r
4425                      GAME_FILT,\r
4426                      title, &number, fileTitle, NULL);\r
4427   if (f != NULL) {\r
4428     cmailMsgLoaded = FALSE;\r
4429     if (number == 0) {\r
4430       int error = GameListBuild(f);\r
4431       if (error) {\r
4432         DisplayError(_("Cannot build game list"), error);\r
4433       } else if (!ListEmpty(&gameList) &&\r
4434                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4435         GameListPopUp(f, fileTitle);\r
4436         return;\r
4437       }\r
4438       GameListDestroy();\r
4439       number = 1;\r
4440     }\r
4441     LoadGame(f, number, fileTitle, FALSE);\r
4442   }\r
4443 }\r
4444 \r
4445 int get_term_width()\r
4446 {\r
4447     HDC hdc;\r
4448     TEXTMETRIC tm;\r
4449     RECT rc;\r
4450     HFONT hfont, hold_font;\r
4451     LOGFONT lf;\r
4452     HWND hText;\r
4453 \r
4454     if (hwndConsole)\r
4455         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4456     else\r
4457         return 79;\r
4458 \r
4459     // get the text metrics\r
4460     hdc = GetDC(hText);\r
4461     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4462     if (consoleCF.dwEffects & CFE_BOLD)\r
4463         lf.lfWeight = FW_BOLD;\r
4464     if (consoleCF.dwEffects & CFE_ITALIC)\r
4465         lf.lfItalic = TRUE;\r
4466     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4467         lf.lfStrikeOut = TRUE;\r
4468     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4469         lf.lfUnderline = TRUE;\r
4470     hfont = CreateFontIndirect(&lf);\r
4471     hold_font = SelectObject(hdc, hfont);\r
4472     GetTextMetrics(hdc, &tm);\r
4473     SelectObject(hdc, hold_font);\r
4474     DeleteObject(hfont);\r
4475     ReleaseDC(hText, hdc);\r
4476 \r
4477     // get the rectangle\r
4478     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4479 \r
4480     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4481 }\r
4482 \r
4483 void UpdateICSWidth(HWND hText)\r
4484 {\r
4485     LONG old_width, new_width;\r
4486 \r
4487     new_width = get_term_width(hText, FALSE);\r
4488     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4489     if (new_width != old_width)\r
4490     {\r
4491         ics_update_width(new_width);\r
4492         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4493     }\r
4494 }\r
4495 \r
4496 VOID\r
4497 ChangedConsoleFont()\r
4498 {\r
4499   CHARFORMAT cfmt;\r
4500   CHARRANGE tmpsel, sel;\r
4501   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4502   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4503   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4504   PARAFORMAT paraf;\r
4505 \r
4506   cfmt.cbSize = sizeof(CHARFORMAT);\r
4507   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4508     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4509                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4510   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4511    * size.  This was undocumented in the version of MSVC++ that I had\r
4512    * when I wrote the code, but is apparently documented now.\r
4513    */\r
4514   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4515   cfmt.bCharSet = f->lf.lfCharSet;\r
4516   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4517   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4518   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4519   /* Why are the following seemingly needed too? */\r
4520   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4521   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4522   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4523   tmpsel.cpMin = 0;\r
4524   tmpsel.cpMax = -1; /*999999?*/\r
4525   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4526   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4527   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4528    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4529    */\r
4530   paraf.cbSize = sizeof(paraf);\r
4531   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4532   paraf.dxStartIndent = 0;\r
4533   paraf.dxOffset = WRAP_INDENT;\r
4534   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4535   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4536   UpdateICSWidth(hText);\r
4537 }\r
4538 \r
4539 /*---------------------------------------------------------------------------*\\r
4540  *\r
4541  * Window Proc for main window\r
4542  *\r
4543 \*---------------------------------------------------------------------------*/\r
4544 \r
4545 /* Process messages for main window, etc. */\r
4546 LRESULT CALLBACK\r
4547 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4548 {\r
4549   FARPROC lpProc;\r
4550   int wmId, wmEvent;\r
4551   char *defName;\r
4552   FILE *f;\r
4553   UINT number;\r
4554   char fileTitle[MSG_SIZ];\r
4555   char buf[MSG_SIZ];\r
4556   static SnapData sd;\r
4557 \r
4558   switch (message) {\r
4559 \r
4560   case WM_PAINT: /* message: repaint portion of window */\r
4561     PaintProc(hwnd);\r
4562     break;\r
4563 \r
4564   case WM_ERASEBKGND:\r
4565     if (IsIconic(hwnd)) {\r
4566       /* Cheat; change the message */\r
4567       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4568     } else {\r
4569       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4570     }\r
4571     break;\r
4572 \r
4573   case WM_LBUTTONDOWN:\r
4574   case WM_MBUTTONDOWN:\r
4575   case WM_RBUTTONDOWN:\r
4576   case WM_LBUTTONUP:\r
4577   case WM_MBUTTONUP:\r
4578   case WM_RBUTTONUP:\r
4579   case WM_MOUSEMOVE:\r
4580   case WM_MOUSEWHEEL:\r
4581     MouseEvent(hwnd, message, wParam, lParam);\r
4582     break;\r
4583 \r
4584   JAWS_KB_NAVIGATION\r
4585 \r
4586   case WM_CHAR:\r
4587     \r
4588     JAWS_ALT_INTERCEPT\r
4589 \r
4590     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4591         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4592         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4593         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4594         SetFocus(h);\r
4595         SendMessage(h, message, wParam, lParam);\r
4596     } else if(lParam != KF_REPEAT) {\r
4597         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4598                 TypeInEvent((char)wParam);\r
4599         } else if((char)wParam == 003) CopyGameToClipboard();\r
4600          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4601     }\r
4602 \r
4603     break;\r
4604 \r
4605   case WM_PALETTECHANGED:\r
4606     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4607       int nnew;\r
4608       HDC hdc = GetDC(hwndMain);\r
4609       SelectPalette(hdc, hPal, TRUE);\r
4610       nnew = RealizePalette(hdc);\r
4611       if (nnew > 0) {\r
4612         paletteChanged = TRUE;\r
4613         InvalidateRect(hwnd, &boardRect, FALSE);\r
4614       }\r
4615       ReleaseDC(hwnd, hdc);\r
4616     }\r
4617     break;\r
4618 \r
4619   case WM_QUERYNEWPALETTE:\r
4620     if (!appData.monoMode /*&& paletteChanged*/) {\r
4621       int nnew;\r
4622       HDC hdc = GetDC(hwndMain);\r
4623       paletteChanged = FALSE;\r
4624       SelectPalette(hdc, hPal, FALSE);\r
4625       nnew = RealizePalette(hdc);\r
4626       if (nnew > 0) {\r
4627         InvalidateRect(hwnd, &boardRect, FALSE);\r
4628       }\r
4629       ReleaseDC(hwnd, hdc);\r
4630       return TRUE;\r
4631     }\r
4632     return FALSE;\r
4633 \r
4634   case WM_COMMAND: /* message: command from application menu */\r
4635     wmId    = LOWORD(wParam);\r
4636     wmEvent = HIWORD(wParam);\r
4637 \r
4638     switch (wmId) {\r
4639     case IDM_NewGame:\r
4640       ResetGameEvent();\r
4641       SAY("new game enter a move to play against the computer with white");\r
4642       break;\r
4643 \r
4644     case IDM_NewGameFRC:\r
4645       if( NewGameFRC() == 0 ) {\r
4646         ResetGameEvent();\r
4647       }\r
4648       break;\r
4649 \r
4650     case IDM_NewVariant:\r
4651       NewVariantPopup(hwnd);\r
4652       break;\r
4653 \r
4654     case IDM_LoadGame:\r
4655       LoadGameDialog(hwnd, _("Load Game from File"));\r
4656       break;\r
4657 \r
4658     case IDM_LoadNextGame:\r
4659       ReloadGame(1);\r
4660       break;\r
4661 \r
4662     case IDM_LoadPrevGame:\r
4663       ReloadGame(-1);\r
4664       break;\r
4665 \r
4666     case IDM_ReloadGame:\r
4667       ReloadGame(0);\r
4668       break;\r
4669 \r
4670     case IDM_LoadPosition:\r
4671       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4672         Reset(FALSE, TRUE);\r
4673       }\r
4674       number = 1;\r
4675       f = OpenFileDialog(hwnd, "rb", "",\r
4676                          appData.oldSaveStyle ? "pos" : "fen",\r
4677                          POSITION_FILT,\r
4678                          _("Load Position from File"), &number, fileTitle, NULL);\r
4679       if (f != NULL) {\r
4680         LoadPosition(f, number, fileTitle);\r
4681       }\r
4682       break;\r
4683 \r
4684     case IDM_LoadNextPosition:\r
4685       ReloadPosition(1);\r
4686       break;\r
4687 \r
4688     case IDM_LoadPrevPosition:\r
4689       ReloadPosition(-1);\r
4690       break;\r
4691 \r
4692     case IDM_ReloadPosition:\r
4693       ReloadPosition(0);\r
4694       break;\r
4695 \r
4696     case IDM_SaveGame:\r
4697       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4698       f = OpenFileDialog(hwnd, "a", defName,\r
4699                          appData.oldSaveStyle ? "gam" : "pgn",\r
4700                          GAME_FILT,\r
4701                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4702       if (f != NULL) {\r
4703         SaveGame(f, 0, "");\r
4704       }\r
4705       break;\r
4706 \r
4707     case IDM_SavePosition:\r
4708       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4709       f = OpenFileDialog(hwnd, "a", defName,\r
4710                          appData.oldSaveStyle ? "pos" : "fen",\r
4711                          POSITION_FILT,\r
4712                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4713       if (f != NULL) {\r
4714         SavePosition(f, 0, "");\r
4715       }\r
4716       break;\r
4717 \r
4718     case IDM_SaveDiagram:\r
4719       defName = "diagram";\r
4720       f = OpenFileDialog(hwnd, "wb", defName,\r
4721                          "bmp",\r
4722                          DIAGRAM_FILT,\r
4723                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4724       if (f != NULL) {\r
4725         SaveDiagram(f);\r
4726       }\r
4727       break;\r
4728 \r
4729     case IDM_CopyGame:\r
4730       CopyGameToClipboard();\r
4731       break;\r
4732 \r
4733     case IDM_PasteGame:\r
4734       PasteGameFromClipboard();\r
4735       break;\r
4736 \r
4737     case IDM_CopyGameListToClipboard:\r
4738       CopyGameListToClipboard();\r
4739       break;\r
4740 \r
4741     /* [AS] Autodetect FEN or PGN data */\r
4742     case IDM_PasteAny:\r
4743       PasteGameOrFENFromClipboard();\r
4744       break;\r
4745 \r
4746     /* [AS] Move history */\r
4747     case IDM_ShowMoveHistory:\r
4748         if( MoveHistoryIsUp() ) {\r
4749             MoveHistoryPopDown();\r
4750         }\r
4751         else {\r
4752             MoveHistoryPopUp();\r
4753         }\r
4754         break;\r
4755 \r
4756     /* [AS] Eval graph */\r
4757     case IDM_ShowEvalGraph:\r
4758         if( EvalGraphIsUp() ) {\r
4759             EvalGraphPopDown();\r
4760         }\r
4761         else {\r
4762             EvalGraphPopUp();\r
4763             SetFocus(hwndMain);\r
4764         }\r
4765         break;\r
4766 \r
4767     /* [AS] Engine output */\r
4768     case IDM_ShowEngineOutput:\r
4769         if( EngineOutputIsUp() ) {\r
4770             EngineOutputPopDown();\r
4771         }\r
4772         else {\r
4773             EngineOutputPopUp();\r
4774         }\r
4775         break;\r
4776 \r
4777     /* [AS] User adjudication */\r
4778     case IDM_UserAdjudication_White:\r
4779         UserAdjudicationEvent( +1 );\r
4780         break;\r
4781 \r
4782     case IDM_UserAdjudication_Black:\r
4783         UserAdjudicationEvent( -1 );\r
4784         break;\r
4785 \r
4786     case IDM_UserAdjudication_Draw:\r
4787         UserAdjudicationEvent( 0 );\r
4788         break;\r
4789 \r
4790     /* [AS] Game list options dialog */\r
4791     case IDM_GameListOptions:\r
4792       GameListOptions();\r
4793       break;\r
4794 \r
4795     case IDM_NewChat:\r
4796       ChatPopUp(NULL);\r
4797       break;\r
4798 \r
4799     case IDM_CopyPosition:\r
4800       CopyFENToClipboard();\r
4801       break;\r
4802 \r
4803     case IDM_PastePosition:\r
4804       PasteFENFromClipboard();\r
4805       break;\r
4806 \r
4807     case IDM_MailMove:\r
4808       MailMoveEvent();\r
4809       break;\r
4810 \r
4811     case IDM_ReloadCMailMsg:\r
4812       Reset(TRUE, TRUE);\r
4813       ReloadCmailMsgEvent(FALSE);\r
4814       break;\r
4815 \r
4816     case IDM_Minimize:\r
4817       ShowWindow(hwnd, SW_MINIMIZE);\r
4818       break;\r
4819 \r
4820     case IDM_Exit:\r
4821       ExitEvent(0);\r
4822       break;\r
4823 \r
4824     case IDM_MachineWhite:\r
4825       MachineWhiteEvent();\r
4826       /*\r
4827        * refresh the tags dialog only if it's visible\r
4828        */\r
4829       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4830           char *tags;\r
4831           tags = PGNTags(&gameInfo);\r
4832           TagsPopUp(tags, CmailMsg());\r
4833           free(tags);\r
4834       }\r
4835       SAY("computer starts playing white");\r
4836       break;\r
4837 \r
4838     case IDM_MachineBlack:\r
4839       MachineBlackEvent();\r
4840       /*\r
4841        * refresh the tags dialog only if it's visible\r
4842        */\r
4843       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4844           char *tags;\r
4845           tags = PGNTags(&gameInfo);\r
4846           TagsPopUp(tags, CmailMsg());\r
4847           free(tags);\r
4848       }\r
4849       SAY("computer starts playing black");\r
4850       break;\r
4851 \r
4852     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4853       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4854       break;\r
4855 \r
4856     case IDM_TwoMachines:\r
4857       TwoMachinesEvent();\r
4858       /*\r
4859        * refresh the tags dialog only if it's visible\r
4860        */\r
4861       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4862           char *tags;\r
4863           tags = PGNTags(&gameInfo);\r
4864           TagsPopUp(tags, CmailMsg());\r
4865           free(tags);\r
4866       }\r
4867       SAY("computer starts playing both sides");\r
4868       break;\r
4869 \r
4870     case IDM_AnalysisMode:\r
4871       if (!first.analysisSupport) {\r
4872         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4873         DisplayError(buf, 0);\r
4874       } else {\r
4875         SAY("analyzing current position");\r
4876         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4877         if (appData.icsActive) {\r
4878                if (gameMode != IcsObserving) {\r
4879                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4880                        DisplayError(buf, 0);\r
4881                        /* secure check */\r
4882                        if (appData.icsEngineAnalyze) {\r
4883                                if (appData.debugMode) \r
4884                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4885                                ExitAnalyzeMode();\r
4886                                ModeHighlight();\r
4887                                break;\r
4888                        }\r
4889                        break;\r
4890                } else {\r
4891                        /* if enable, user want disable icsEngineAnalyze */\r
4892                        if (appData.icsEngineAnalyze) {\r
4893                                ExitAnalyzeMode();\r
4894                                ModeHighlight();\r
4895                                break;\r
4896                        }\r
4897                        appData.icsEngineAnalyze = TRUE;\r
4898                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4899                }\r
4900         } \r
4901         if (!appData.showThinking) ToggleShowThinking();\r
4902         AnalyzeModeEvent();\r
4903       }\r
4904       break;\r
4905 \r
4906     case IDM_AnalyzeFile:\r
4907       if (!first.analysisSupport) {\r
4908         char buf[MSG_SIZ];\r
4909           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4910         DisplayError(buf, 0);\r
4911       } else {\r
4912         if (!appData.showThinking) ToggleShowThinking();\r
4913         AnalyzeFileEvent();\r
4914         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4915         AnalysisPeriodicEvent(1);\r
4916       }\r
4917       break;\r
4918 \r
4919     case IDM_IcsClient:\r
4920       IcsClientEvent();\r
4921       break;\r
4922 \r
4923     case IDM_EditGame:\r
4924     case IDM_EditGame2:\r
4925       EditGameEvent();\r
4926       SAY("edit game");\r
4927       break;\r
4928 \r
4929     case IDM_EditPosition:\r
4930     case IDM_EditPosition2:\r
4931       EditPositionEvent();\r
4932       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4933       break;\r
4934 \r
4935     case IDM_Training:\r
4936       TrainingEvent();\r
4937       break;\r
4938 \r
4939     case IDM_ShowGameList:\r
4940       ShowGameListProc();\r
4941       break;\r
4942 \r
4943     case IDM_EditProgs1:\r
4944       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4945       break;\r
4946 \r
4947     case IDM_EditProgs2:\r
4948      LoadEnginePopUp(hwndMain);\r
4949 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4950       break;\r
4951 \r
4952     case IDM_EditServers:\r
4953       EditTagsPopUp(icsNames, &icsNames);\r
4954       break;\r
4955 \r
4956     case IDM_EditTags:\r
4957     case IDM_Tags:\r
4958       EditTagsProc();\r
4959       break;\r
4960 \r
4961     case IDM_EditComment:\r
4962     case IDM_Comment:\r
4963       if (commentUp && editComment) {\r
4964         CommentPopDown();\r
4965       } else {\r
4966         EditCommentEvent();\r
4967       }\r
4968       break;\r
4969 \r
4970     case IDM_Pause:\r
4971       PauseEvent();\r
4972       break;\r
4973 \r
4974     case IDM_Accept:\r
4975       AcceptEvent();\r
4976       break;\r
4977 \r
4978     case IDM_Decline:\r
4979       DeclineEvent();\r
4980       break;\r
4981 \r
4982     case IDM_Rematch:\r
4983       RematchEvent();\r
4984       break;\r
4985 \r
4986     case IDM_CallFlag:\r
4987       CallFlagEvent();\r
4988       break;\r
4989 \r
4990     case IDM_Draw:\r
4991       DrawEvent();\r
4992       break;\r
4993 \r
4994     case IDM_Adjourn:\r
4995       AdjournEvent();\r
4996       break;\r
4997 \r
4998     case IDM_Abort:\r
4999       AbortEvent();\r
5000       break;\r
5001 \r
5002     case IDM_Resign:\r
5003       ResignEvent();\r
5004       break;\r
5005 \r
5006     case IDM_StopObserving:\r
5007       StopObservingEvent();\r
5008       break;\r
5009 \r
5010     case IDM_StopExamining:\r
5011       StopExaminingEvent();\r
5012       break;\r
5013 \r
5014     case IDM_Upload:\r
5015       UploadGameEvent();\r
5016       break;\r
5017 \r
5018     case IDM_TypeInMove:\r
5019       TypeInEvent('\000');\r
5020       break;\r
5021 \r
5022     case IDM_TypeInName:\r
5023       PopUpNameDialog('\000');\r
5024       break;\r
5025 \r
5026     case IDM_Backward:\r
5027       BackwardEvent();\r
5028       SetFocus(hwndMain);\r
5029       break;\r
5030 \r
5031     JAWS_MENU_ITEMS\r
5032 \r
5033     case IDM_Forward:\r
5034       ForwardEvent();\r
5035       SetFocus(hwndMain);\r
5036       break;\r
5037 \r
5038     case IDM_ToStart:\r
5039       ToStartEvent();\r
5040       SetFocus(hwndMain);\r
5041       break;\r
5042 \r
5043     case IDM_ToEnd:\r
5044       ToEndEvent();\r
5045       SetFocus(hwndMain);\r
5046       break;\r
5047 \r
5048     case IDM_Revert:\r
5049       RevertEvent(FALSE);\r
5050       break;\r
5051 \r
5052     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5053       RevertEvent(TRUE);\r
5054       break;\r
5055 \r
5056     case IDM_TruncateGame:\r
5057       TruncateGameEvent();\r
5058       break;\r
5059 \r
5060     case IDM_MoveNow:\r
5061       MoveNowEvent();\r
5062       break;\r
5063 \r
5064     case IDM_RetractMove:\r
5065       RetractMoveEvent();\r
5066       break;\r
5067 \r
5068     case IDM_FlipView:\r
5069       flipView = !flipView;\r
5070       DrawPosition(FALSE, NULL);\r
5071       break;\r
5072 \r
5073     case IDM_FlipClock:\r
5074       flipClock = !flipClock;\r
5075       DisplayBothClocks();\r
5076       DisplayLogos();\r
5077       break;\r
5078 \r
5079     case IDM_MuteSounds:\r
5080       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5081       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5082                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5083       break;\r
5084 \r
5085     case IDM_GeneralOptions:\r
5086       GeneralOptionsPopup(hwnd);\r
5087       DrawPosition(TRUE, NULL);\r
5088       break;\r
5089 \r
5090     case IDM_BoardOptions:\r
5091       BoardOptionsPopup(hwnd);\r
5092       break;\r
5093 \r
5094     case IDM_EnginePlayOptions:\r
5095       EnginePlayOptionsPopup(hwnd);\r
5096       break;\r
5097 \r
5098     case IDM_Engine1Options:\r
5099       EngineOptionsPopup(hwnd, &first);\r
5100       break;\r
5101 \r
5102     case IDM_Engine2Options:\r
5103       savedHwnd = hwnd;\r
5104       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5105       EngineOptionsPopup(hwnd, &second);\r
5106       break;\r
5107 \r
5108     case IDM_OptionsUCI:\r
5109       UciOptionsPopup(hwnd);\r
5110       break;\r
5111 \r
5112     case IDM_Tourney:\r
5113       TourneyPopup(hwnd);\r
5114       break;\r
5115 \r
5116     case IDM_IcsOptions:\r
5117       IcsOptionsPopup(hwnd);\r
5118       break;\r
5119 \r
5120     case IDM_Fonts:\r
5121       FontsOptionsPopup(hwnd);\r
5122       break;\r
5123 \r
5124     case IDM_Sounds:\r
5125       SoundOptionsPopup(hwnd);\r
5126       break;\r
5127 \r
5128     case IDM_CommPort:\r
5129       CommPortOptionsPopup(hwnd);\r
5130       break;\r
5131 \r
5132     case IDM_LoadOptions:\r
5133       LoadOptionsPopup(hwnd);\r
5134       break;\r
5135 \r
5136     case IDM_SaveOptions:\r
5137       SaveOptionsPopup(hwnd);\r
5138       break;\r
5139 \r
5140     case IDM_TimeControl:\r
5141       TimeControlOptionsPopup(hwnd);\r
5142       break;\r
5143 \r
5144     case IDM_SaveSettings:\r
5145       SaveSettings(settingsFileName);\r
5146       break;\r
5147 \r
5148     case IDM_SaveSettingsOnExit:\r
5149       saveSettingsOnExit = !saveSettingsOnExit;\r
5150       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5151                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5152                                          MF_CHECKED : MF_UNCHECKED));\r
5153       break;\r
5154 \r
5155     case IDM_Hint:\r
5156       HintEvent();\r
5157       break;\r
5158 \r
5159     case IDM_Book:\r
5160       BookEvent();\r
5161       break;\r
5162 \r
5163     case IDM_AboutGame:\r
5164       AboutGameEvent();\r
5165       break;\r
5166 \r
5167     case IDM_Debug:\r
5168       appData.debugMode = !appData.debugMode;\r
5169       if (appData.debugMode) {\r
5170         char dir[MSG_SIZ];\r
5171         GetCurrentDirectory(MSG_SIZ, dir);\r
5172         SetCurrentDirectory(installDir);\r
5173         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5174         SetCurrentDirectory(dir);\r
5175         setbuf(debugFP, NULL);\r
5176       } else {\r
5177         fclose(debugFP);\r
5178         debugFP = NULL;\r
5179       }\r
5180       break;\r
5181 \r
5182     case IDM_HELPCONTENTS:\r
5183       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5184           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5185           MessageBox (GetFocus(),\r
5186                     _("Unable to activate help"),\r
5187                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5188       }\r
5189       break;\r
5190 \r
5191     case IDM_HELPSEARCH:\r
5192         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5193             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5194         MessageBox (GetFocus(),\r
5195                     _("Unable to activate help"),\r
5196                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5197       }\r
5198       break;\r
5199 \r
5200     case IDM_HELPHELP:\r
5201       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5202         MessageBox (GetFocus(),\r
5203                     _("Unable to activate help"),\r
5204                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5205       }\r
5206       break;\r
5207 \r
5208     case IDM_ABOUT:\r
5209       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5210       DialogBox(hInst, \r
5211         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5212         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5213       FreeProcInstance(lpProc);\r
5214       break;\r
5215 \r
5216     case IDM_DirectCommand1:\r
5217       AskQuestionEvent(_("Direct Command"),\r
5218                        _("Send to chess program:"), "", "1");\r
5219       break;\r
5220     case IDM_DirectCommand2:\r
5221       AskQuestionEvent(_("Direct Command"),\r
5222                        _("Send to second chess program:"), "", "2");\r
5223       break;\r
5224 \r
5225     case EP_WhitePawn:\r
5226       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5227       fromX = fromY = -1;\r
5228       break;\r
5229 \r
5230     case EP_WhiteKnight:\r
5231       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5232       fromX = fromY = -1;\r
5233       break;\r
5234 \r
5235     case EP_WhiteBishop:\r
5236       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5237       fromX = fromY = -1;\r
5238       break;\r
5239 \r
5240     case EP_WhiteRook:\r
5241       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5242       fromX = fromY = -1;\r
5243       break;\r
5244 \r
5245     case EP_WhiteQueen:\r
5246       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5247       fromX = fromY = -1;\r
5248       break;\r
5249 \r
5250     case EP_WhiteFerz:\r
5251       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5252       fromX = fromY = -1;\r
5253       break;\r
5254 \r
5255     case EP_WhiteWazir:\r
5256       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5257       fromX = fromY = -1;\r
5258       break;\r
5259 \r
5260     case EP_WhiteAlfil:\r
5261       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5262       fromX = fromY = -1;\r
5263       break;\r
5264 \r
5265     case EP_WhiteCannon:\r
5266       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5267       fromX = fromY = -1;\r
5268       break;\r
5269 \r
5270     case EP_WhiteCardinal:\r
5271       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5272       fromX = fromY = -1;\r
5273       break;\r
5274 \r
5275     case EP_WhiteMarshall:\r
5276       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5277       fromX = fromY = -1;\r
5278       break;\r
5279 \r
5280     case EP_WhiteKing:\r
5281       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5282       fromX = fromY = -1;\r
5283       break;\r
5284 \r
5285     case EP_BlackPawn:\r
5286       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5287       fromX = fromY = -1;\r
5288       break;\r
5289 \r
5290     case EP_BlackKnight:\r
5291       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5292       fromX = fromY = -1;\r
5293       break;\r
5294 \r
5295     case EP_BlackBishop:\r
5296       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5297       fromX = fromY = -1;\r
5298       break;\r
5299 \r
5300     case EP_BlackRook:\r
5301       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5302       fromX = fromY = -1;\r
5303       break;\r
5304 \r
5305     case EP_BlackQueen:\r
5306       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5307       fromX = fromY = -1;\r
5308       break;\r
5309 \r
5310     case EP_BlackFerz:\r
5311       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5312       fromX = fromY = -1;\r
5313       break;\r
5314 \r
5315     case EP_BlackWazir:\r
5316       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5317       fromX = fromY = -1;\r
5318       break;\r
5319 \r
5320     case EP_BlackAlfil:\r
5321       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5322       fromX = fromY = -1;\r
5323       break;\r
5324 \r
5325     case EP_BlackCannon:\r
5326       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5327       fromX = fromY = -1;\r
5328       break;\r
5329 \r
5330     case EP_BlackCardinal:\r
5331       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5332       fromX = fromY = -1;\r
5333       break;\r
5334 \r
5335     case EP_BlackMarshall:\r
5336       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5337       fromX = fromY = -1;\r
5338       break;\r
5339 \r
5340     case EP_BlackKing:\r
5341       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5342       fromX = fromY = -1;\r
5343       break;\r
5344 \r
5345     case EP_EmptySquare:\r
5346       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5347       fromX = fromY = -1;\r
5348       break;\r
5349 \r
5350     case EP_ClearBoard:\r
5351       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5352       fromX = fromY = -1;\r
5353       break;\r
5354 \r
5355     case EP_White:\r
5356       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5357       fromX = fromY = -1;\r
5358       break;\r
5359 \r
5360     case EP_Black:\r
5361       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5362       fromX = fromY = -1;\r
5363       break;\r
5364 \r
5365     case EP_Promote:\r
5366       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5367       fromX = fromY = -1;\r
5368       break;\r
5369 \r
5370     case EP_Demote:\r
5371       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5372       fromX = fromY = -1;\r
5373       break;\r
5374 \r
5375     case DP_Pawn:\r
5376       DropMenuEvent(WhitePawn, fromX, fromY);\r
5377       fromX = fromY = -1;\r
5378       break;\r
5379 \r
5380     case DP_Knight:\r
5381       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5382       fromX = fromY = -1;\r
5383       break;\r
5384 \r
5385     case DP_Bishop:\r
5386       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5387       fromX = fromY = -1;\r
5388       break;\r
5389 \r
5390     case DP_Rook:\r
5391       DropMenuEvent(WhiteRook, fromX, fromY);\r
5392       fromX = fromY = -1;\r
5393       break;\r
5394 \r
5395     case DP_Queen:\r
5396       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5397       fromX = fromY = -1;\r
5398       break;\r
5399 \r
5400     case IDM_English:\r
5401       barbaric = 0; appData.language = "";\r
5402       TranslateMenus(0);\r
5403       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5404       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5405       lastChecked = wmId;\r
5406       break;\r
5407 \r
5408     default:\r
5409       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5410           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5411           TranslateMenus(0);\r
5412           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5413           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5414           lastChecked = wmId;\r
5415           break;\r
5416       }\r
5417       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5418     }\r
5419     break;\r
5420 \r
5421   case WM_TIMER:\r
5422     switch (wParam) {\r
5423     case CLOCK_TIMER_ID:\r
5424       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5425       clockTimerEvent = 0;\r
5426       DecrementClocks(); /* call into back end */\r
5427       break;\r
5428     case LOAD_GAME_TIMER_ID:\r
5429       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5430       loadGameTimerEvent = 0;\r
5431       AutoPlayGameLoop(); /* call into back end */\r
5432       break;\r
5433     case ANALYSIS_TIMER_ID:\r
5434       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5435                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5436         AnalysisPeriodicEvent(0);\r
5437       } else {\r
5438         KillTimer(hwnd, analysisTimerEvent);\r
5439         analysisTimerEvent = 0;\r
5440       }\r
5441       break;\r
5442     case DELAYED_TIMER_ID:\r
5443       KillTimer(hwnd, delayedTimerEvent);\r
5444       delayedTimerEvent = 0;\r
5445       delayedTimerCallback();\r
5446       break;\r
5447     }\r
5448     break;\r
5449 \r
5450   case WM_USER_Input:\r
5451     InputEvent(hwnd, message, wParam, lParam);\r
5452     break;\r
5453 \r
5454   /* [AS] Also move "attached" child windows */\r
5455   case WM_WINDOWPOSCHANGING:\r
5456 \r
5457     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5458         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5459 \r
5460         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5461             /* Window is moving */\r
5462             RECT rcMain;\r
5463 \r
5464 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5465             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5466             rcMain.right  = wpMain.x + wpMain.width;\r
5467             rcMain.top    = wpMain.y;\r
5468             rcMain.bottom = wpMain.y + wpMain.height;\r
5469             \r
5470             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5471             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5472             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5473             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5474             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5475             wpMain.x = lpwp->x;\r
5476             wpMain.y = lpwp->y;\r
5477         }\r
5478     }\r
5479     break;\r
5480 \r
5481   /* [AS] Snapping */\r
5482   case WM_ENTERSIZEMOVE:\r
5483     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5484     if (hwnd == hwndMain) {\r
5485       doingSizing = TRUE;\r
5486       lastSizing = 0;\r
5487     }\r
5488     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5489     break;\r
5490 \r
5491   case WM_SIZING:\r
5492     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5493     if (hwnd == hwndMain) {\r
5494       lastSizing = wParam;\r
5495     }\r
5496     break;\r
5497 \r
5498   case WM_MOVING:\r
5499     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5500       return OnMoving( &sd, hwnd, wParam, lParam );\r
5501 \r
5502   case WM_EXITSIZEMOVE:\r
5503     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5504     if (hwnd == hwndMain) {\r
5505       RECT client;\r
5506       doingSizing = FALSE;\r
5507       InvalidateRect(hwnd, &boardRect, FALSE);\r
5508       GetClientRect(hwnd, &client);\r
5509       ResizeBoard(client.right, client.bottom, lastSizing);\r
5510       lastSizing = 0;\r
5511       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5512     }\r
5513     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5514     break;\r
5515 \r
5516   case WM_DESTROY: /* message: window being destroyed */\r
5517     PostQuitMessage(0);\r
5518     break;\r
5519 \r
5520   case WM_CLOSE:\r
5521     if (hwnd == hwndMain) {\r
5522       ExitEvent(0);\r
5523     }\r
5524     break;\r
5525 \r
5526   default:      /* Passes it on if unprocessed */\r
5527     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5528   }\r
5529   return 0;\r
5530 }\r
5531 \r
5532 /*---------------------------------------------------------------------------*\\r
5533  *\r
5534  * Misc utility routines\r
5535  *\r
5536 \*---------------------------------------------------------------------------*/\r
5537 \r
5538 /*\r
5539  * Decent random number generator, at least not as bad as Windows\r
5540  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5541  */\r
5542 unsigned int randstate;\r
5543 \r
5544 int\r
5545 myrandom(void)\r
5546 {\r
5547   randstate = randstate * 1664525 + 1013904223;\r
5548   return (int) randstate & 0x7fffffff;\r
5549 }\r
5550 \r
5551 void\r
5552 mysrandom(unsigned int seed)\r
5553 {\r
5554   randstate = seed;\r
5555 }\r
5556 \r
5557 \r
5558 /* \r
5559  * returns TRUE if user selects a different color, FALSE otherwise \r
5560  */\r
5561 \r
5562 BOOL\r
5563 ChangeColor(HWND hwnd, COLORREF *which)\r
5564 {\r
5565   static BOOL firstTime = TRUE;\r
5566   static DWORD customColors[16];\r
5567   CHOOSECOLOR cc;\r
5568   COLORREF newcolor;\r
5569   int i;\r
5570   ColorClass ccl;\r
5571 \r
5572   if (firstTime) {\r
5573     /* Make initial colors in use available as custom colors */\r
5574     /* Should we put the compiled-in defaults here instead? */\r
5575     i = 0;\r
5576     customColors[i++] = lightSquareColor & 0xffffff;\r
5577     customColors[i++] = darkSquareColor & 0xffffff;\r
5578     customColors[i++] = whitePieceColor & 0xffffff;\r
5579     customColors[i++] = blackPieceColor & 0xffffff;\r
5580     customColors[i++] = highlightSquareColor & 0xffffff;\r
5581     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5582 \r
5583     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5584       customColors[i++] = textAttribs[ccl].color;\r
5585     }\r
5586     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5587     firstTime = FALSE;\r
5588   }\r
5589 \r
5590   cc.lStructSize = sizeof(cc);\r
5591   cc.hwndOwner = hwnd;\r
5592   cc.hInstance = NULL;\r
5593   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5594   cc.lpCustColors = (LPDWORD) customColors;\r
5595   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5596 \r
5597   if (!ChooseColor(&cc)) return FALSE;\r
5598 \r
5599   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5600   if (newcolor == *which) return FALSE;\r
5601   *which = newcolor;\r
5602   return TRUE;\r
5603 \r
5604   /*\r
5605   InitDrawingColors();\r
5606   InvalidateRect(hwnd, &boardRect, FALSE);\r
5607   */\r
5608 }\r
5609 \r
5610 BOOLEAN\r
5611 MyLoadSound(MySound *ms)\r
5612 {\r
5613   BOOL ok = FALSE;\r
5614   struct stat st;\r
5615   FILE *f;\r
5616 \r
5617   if (ms->data) free(ms->data);\r
5618   ms->data = NULL;\r
5619 \r
5620   switch (ms->name[0]) {\r
5621   case NULLCHAR:\r
5622     /* Silence */\r
5623     ok = TRUE;\r
5624     break;\r
5625   case '$':\r
5626     /* System sound from Control Panel.  Don't preload here. */\r
5627     ok = TRUE;\r
5628     break;\r
5629   case '!':\r
5630     if (ms->name[1] == NULLCHAR) {\r
5631       /* "!" alone = silence */\r
5632       ok = TRUE;\r
5633     } else {\r
5634       /* Builtin wave resource.  Error if not found. */\r
5635       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5636       if (h == NULL) break;\r
5637       ms->data = (void *)LoadResource(hInst, h);\r
5638       if (h == NULL) break;\r
5639       ok = TRUE;\r
5640     }\r
5641     break;\r
5642   default:\r
5643     /* .wav file.  Error if not found. */\r
5644     f = fopen(ms->name, "rb");\r
5645     if (f == NULL) break;\r
5646     if (fstat(fileno(f), &st) < 0) break;\r
5647     ms->data = malloc(st.st_size);\r
5648     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5649     fclose(f);\r
5650     ok = TRUE;\r
5651     break;\r
5652   }\r
5653   if (!ok) {\r
5654     char buf[MSG_SIZ];\r
5655       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5656     DisplayError(buf, GetLastError());\r
5657   }\r
5658   return ok;\r
5659 }\r
5660 \r
5661 BOOLEAN\r
5662 MyPlaySound(MySound *ms)\r
5663 {\r
5664   BOOLEAN ok = FALSE;\r
5665 \r
5666   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5667   switch (ms->name[0]) {\r
5668   case NULLCHAR:\r
5669         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5670     /* Silence */\r
5671     ok = TRUE;\r
5672     break;\r
5673   case '$':\r
5674     /* System sound from Control Panel (deprecated feature).\r
5675        "$" alone or an unset sound name gets default beep (still in use). */\r
5676     if (ms->name[1]) {\r
5677       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5678     }\r
5679     if (!ok) ok = MessageBeep(MB_OK);\r
5680     break; \r
5681   case '!':\r
5682     /* Builtin wave resource, or "!" alone for silence */\r
5683     if (ms->name[1]) {\r
5684       if (ms->data == NULL) return FALSE;\r
5685       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5686     } else {\r
5687       ok = TRUE;\r
5688     }\r
5689     break;\r
5690   default:\r
5691     /* .wav file.  Error if not found. */\r
5692     if (ms->data == NULL) return FALSE;\r
5693     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5694     break;\r
5695   }\r
5696   /* Don't print an error: this can happen innocently if the sound driver\r
5697      is busy; for instance, if another instance of WinBoard is playing\r
5698      a sound at about the same time. */\r
5699   return ok;\r
5700 }\r
5701 \r
5702 \r
5703 LRESULT CALLBACK\r
5704 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5705 {\r
5706   BOOL ok;\r
5707   OPENFILENAME *ofn;\r
5708   static UINT *number; /* gross that this is static */\r
5709 \r
5710   switch (message) {\r
5711   case WM_INITDIALOG: /* message: initialize dialog box */\r
5712     /* Center the dialog over the application window */\r
5713     ofn = (OPENFILENAME *) lParam;\r
5714     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5715       number = (UINT *) ofn->lCustData;\r
5716       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5717     } else {\r
5718       number = NULL;\r
5719     }\r
5720     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5721     Translate(hDlg, 1536);\r
5722     return FALSE;  /* Allow for further processing */\r
5723 \r
5724   case WM_COMMAND:\r
5725     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5726       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5727     }\r
5728     return FALSE;  /* Allow for further processing */\r
5729   }\r
5730   return FALSE;\r
5731 }\r
5732 \r
5733 UINT APIENTRY\r
5734 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5735 {\r
5736   static UINT *number;\r
5737   OPENFILENAME *ofname;\r
5738   OFNOTIFY *ofnot;\r
5739   switch (uiMsg) {\r
5740   case WM_INITDIALOG:\r
5741     Translate(hdlg, DLG_IndexNumber);\r
5742     ofname = (OPENFILENAME *)lParam;\r
5743     number = (UINT *)(ofname->lCustData);\r
5744     break;\r
5745   case WM_NOTIFY:\r
5746     ofnot = (OFNOTIFY *)lParam;\r
5747     if (ofnot->hdr.code == CDN_FILEOK) {\r
5748       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5749     }\r
5750     break;\r
5751   }\r
5752   return 0;\r
5753 }\r
5754 \r
5755 \r
5756 FILE *\r
5757 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5758                char *nameFilt, char *dlgTitle, UINT *number,\r
5759                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5760 {\r
5761   OPENFILENAME openFileName;\r
5762   char buf1[MSG_SIZ];\r
5763   FILE *f;\r
5764 \r
5765   if (fileName == NULL) fileName = buf1;\r
5766   if (defName == NULL) {\r
5767     safeStrCpy(fileName, "*.", 3 );\r
5768     strcat(fileName, defExt);\r
5769   } else {\r
5770     safeStrCpy(fileName, defName, MSG_SIZ );\r
5771   }\r
5772     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5773   if (number) *number = 0;\r
5774 \r
5775   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5776   openFileName.hwndOwner         = hwnd;\r
5777   openFileName.hInstance         = (HANDLE) hInst;\r
5778   openFileName.lpstrFilter       = nameFilt;\r
5779   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5780   openFileName.nMaxCustFilter    = 0L;\r
5781   openFileName.nFilterIndex      = 1L;\r
5782   openFileName.lpstrFile         = fileName;\r
5783   openFileName.nMaxFile          = MSG_SIZ;\r
5784   openFileName.lpstrFileTitle    = fileTitle;\r
5785   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5786   openFileName.lpstrInitialDir   = NULL;\r
5787   openFileName.lpstrTitle        = dlgTitle;\r
5788   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5789     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5790     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5791     | (oldDialog ? 0 : OFN_EXPLORER);\r
5792   openFileName.nFileOffset       = 0;\r
5793   openFileName.nFileExtension    = 0;\r
5794   openFileName.lpstrDefExt       = defExt;\r
5795   openFileName.lCustData         = (LONG) number;\r
5796   openFileName.lpfnHook          = oldDialog ?\r
5797     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5798   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5799 \r
5800   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5801                         GetOpenFileName(&openFileName)) {\r
5802     /* open the file */\r
5803     f = fopen(openFileName.lpstrFile, write);\r
5804     if (f == NULL) {\r
5805       MessageBox(hwnd, _("File open failed"), NULL,\r
5806                  MB_OK|MB_ICONEXCLAMATION);\r
5807       return NULL;\r
5808     }\r
5809   } else {\r
5810     int err = CommDlgExtendedError();\r
5811     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5812     return FALSE;\r
5813   }\r
5814   return f;\r
5815 }\r
5816 \r
5817 \r
5818 \r
5819 VOID APIENTRY\r
5820 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5821 {\r
5822   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5823 \r
5824   /*\r
5825    * Get the first pop-up menu in the menu template. This is the\r
5826    * menu that TrackPopupMenu displays.\r
5827    */\r
5828   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5829   TranslateOneMenu(10, hmenuTrackPopup);\r
5830 \r
5831   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5832 \r
5833   /*\r
5834    * TrackPopup uses screen coordinates, so convert the\r
5835    * coordinates of the mouse click to screen coordinates.\r
5836    */\r
5837   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5838 \r
5839   /* Draw and track the floating pop-up menu. */\r
5840   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5841                  pt.x, pt.y, 0, hwnd, NULL);\r
5842 \r
5843   /* Destroy the menu.*/\r
5844   DestroyMenu(hmenu);\r
5845 }\r
5846    \r
5847 typedef struct {\r
5848   HWND hDlg, hText;\r
5849   int sizeX, sizeY, newSizeX, newSizeY;\r
5850   HDWP hdwp;\r
5851 } ResizeEditPlusButtonsClosure;\r
5852 \r
5853 BOOL CALLBACK\r
5854 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5855 {\r
5856   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5857   RECT rect;\r
5858   POINT pt;\r
5859 \r
5860   if (hChild == cl->hText) return TRUE;\r
5861   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5862   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5863   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5864   ScreenToClient(cl->hDlg, &pt);\r
5865   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5866     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5867   return TRUE;\r
5868 }\r
5869 \r
5870 /* Resize a dialog that has a (rich) edit field filling most of\r
5871    the top, with a row of buttons below */\r
5872 VOID\r
5873 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5874 {\r
5875   RECT rectText;\r
5876   int newTextHeight, newTextWidth;\r
5877   ResizeEditPlusButtonsClosure cl;\r
5878   \r
5879   /*if (IsIconic(hDlg)) return;*/\r
5880   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5881   \r
5882   cl.hdwp = BeginDeferWindowPos(8);\r
5883 \r
5884   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5885   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5886   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5887   if (newTextHeight < 0) {\r
5888     newSizeY += -newTextHeight;\r
5889     newTextHeight = 0;\r
5890   }\r
5891   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5892     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5893 \r
5894   cl.hDlg = hDlg;\r
5895   cl.hText = hText;\r
5896   cl.sizeX = sizeX;\r
5897   cl.sizeY = sizeY;\r
5898   cl.newSizeX = newSizeX;\r
5899   cl.newSizeY = newSizeY;\r
5900   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5901 \r
5902   EndDeferWindowPos(cl.hdwp);\r
5903 }\r
5904 \r
5905 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5906 {\r
5907     RECT    rChild, rParent;\r
5908     int     wChild, hChild, wParent, hParent;\r
5909     int     wScreen, hScreen, xNew, yNew;\r
5910     HDC     hdc;\r
5911 \r
5912     /* Get the Height and Width of the child window */\r
5913     GetWindowRect (hwndChild, &rChild);\r
5914     wChild = rChild.right - rChild.left;\r
5915     hChild = rChild.bottom - rChild.top;\r
5916 \r
5917     /* Get the Height and Width of the parent window */\r
5918     GetWindowRect (hwndParent, &rParent);\r
5919     wParent = rParent.right - rParent.left;\r
5920     hParent = rParent.bottom - rParent.top;\r
5921 \r
5922     /* Get the display limits */\r
5923     hdc = GetDC (hwndChild);\r
5924     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5925     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5926     ReleaseDC(hwndChild, hdc);\r
5927 \r
5928     /* Calculate new X position, then adjust for screen */\r
5929     xNew = rParent.left + ((wParent - wChild) /2);\r
5930     if (xNew < 0) {\r
5931         xNew = 0;\r
5932     } else if ((xNew+wChild) > wScreen) {\r
5933         xNew = wScreen - wChild;\r
5934     }\r
5935 \r
5936     /* Calculate new Y position, then adjust for screen */\r
5937     if( mode == 0 ) {\r
5938         yNew = rParent.top  + ((hParent - hChild) /2);\r
5939     }\r
5940     else {\r
5941         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5942     }\r
5943 \r
5944     if (yNew < 0) {\r
5945         yNew = 0;\r
5946     } else if ((yNew+hChild) > hScreen) {\r
5947         yNew = hScreen - hChild;\r
5948     }\r
5949 \r
5950     /* Set it, and return */\r
5951     return SetWindowPos (hwndChild, NULL,\r
5952                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5953 }\r
5954 \r
5955 /* Center one window over another */\r
5956 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5957 {\r
5958     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5959 }\r
5960 \r
5961 /*---------------------------------------------------------------------------*\\r
5962  *\r
5963  * Startup Dialog functions\r
5964  *\r
5965 \*---------------------------------------------------------------------------*/\r
5966 void\r
5967 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5968 {\r
5969   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5970 \r
5971   while (*cd != NULL) {\r
5972     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5973     cd++;\r
5974   }\r
5975 }\r
5976 \r
5977 void\r
5978 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5979 {\r
5980   char buf1[MAX_ARG_LEN];\r
5981   int len;\r
5982 \r
5983   if (str[0] == '@') {\r
5984     FILE* f = fopen(str + 1, "r");\r
5985     if (f == NULL) {\r
5986       DisplayFatalError(str + 1, errno, 2);\r
5987       return;\r
5988     }\r
5989     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5990     fclose(f);\r
5991     buf1[len] = NULLCHAR;\r
5992     str = buf1;\r
5993   }\r
5994 \r
5995   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5996 \r
5997   for (;;) {\r
5998     char buf[MSG_SIZ];\r
5999     char *end = strchr(str, '\n');\r
6000     if (end == NULL) return;\r
6001     memcpy(buf, str, end - str);\r
6002     buf[end - str] = NULLCHAR;\r
6003     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6004     str = end + 1;\r
6005   }\r
6006 }\r
6007 \r
6008 void\r
6009 SetStartupDialogEnables(HWND hDlg)\r
6010 {\r
6011   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6012     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6013     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6014   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6015     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6016   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6017     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6018   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6019     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6020   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6021     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6022     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6023     IsDlgButtonChecked(hDlg, OPT_View));\r
6024 }\r
6025 \r
6026 char *\r
6027 QuoteForFilename(char *filename)\r
6028 {\r
6029   int dquote, space;\r
6030   dquote = strchr(filename, '"') != NULL;\r
6031   space = strchr(filename, ' ') != NULL;\r
6032   if (dquote || space) {\r
6033     if (dquote) {\r
6034       return "'";\r
6035     } else {\r
6036       return "\"";\r
6037     }\r
6038   } else {\r
6039     return "";\r
6040   }\r
6041 }\r
6042 \r
6043 VOID\r
6044 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6045 {\r
6046   char buf[MSG_SIZ];\r
6047   char *q;\r
6048 \r
6049   InitComboStringsFromOption(hwndCombo, nthnames);\r
6050   q = QuoteForFilename(nthcp);\r
6051     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6052   if (*nthdir != NULLCHAR) {\r
6053     q = QuoteForFilename(nthdir);\r
6054       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6055   }\r
6056   if (*nthcp == NULLCHAR) {\r
6057     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6058   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6059     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6060     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6061   }\r
6062 }\r
6063 \r
6064 LRESULT CALLBACK\r
6065 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6066 {\r
6067   char buf[MSG_SIZ];\r
6068   HANDLE hwndCombo;\r
6069   char *p;\r
6070 \r
6071   switch (message) {\r
6072   case WM_INITDIALOG:\r
6073     /* Center the dialog */\r
6074     CenterWindow (hDlg, GetDesktopWindow());\r
6075     Translate(hDlg, DLG_Startup);\r
6076     /* Initialize the dialog items */\r
6077     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6078                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6079                   firstChessProgramNames);\r
6080     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6081                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6082                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6083     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6084     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6085       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6086     if (*appData.icsHelper != NULLCHAR) {\r
6087       char *q = QuoteForFilename(appData.icsHelper);\r
6088       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6089     }\r
6090     if (*appData.icsHost == NULLCHAR) {\r
6091       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6092       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6093     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6094       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6095       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6096     }\r
6097 \r
6098     if (appData.icsActive) {\r
6099       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6100     }\r
6101     else if (appData.noChessProgram) {\r
6102       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6103     }\r
6104     else {\r
6105       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6106     }\r
6107 \r
6108     SetStartupDialogEnables(hDlg);\r
6109     return TRUE;\r
6110 \r
6111   case WM_COMMAND:\r
6112     switch (LOWORD(wParam)) {\r
6113     case IDOK:\r
6114       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6115         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6116         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6117         p = buf;\r
6118         ParseArgs(StringGet, &p);\r
6119         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6120         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6121         p = buf;
6122         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6123         ParseArgs(StringGet, &p);\r
6124         SwapEngines(singleList); // ... and then make it 'second'\r
6125         appData.noChessProgram = FALSE;\r
6126         appData.icsActive = FALSE;\r
6127       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6128         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6129         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6130         p = buf;\r
6131         ParseArgs(StringGet, &p);\r
6132         if (appData.zippyPlay) {\r
6133           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6134           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6135           p = buf;\r
6136           ParseArgs(StringGet, &p);\r
6137         }\r
6138       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6139         appData.noChessProgram = TRUE;\r
6140         appData.icsActive = FALSE;\r
6141       } else {\r
6142         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6143                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6144         return TRUE;\r
6145       }\r
6146       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6147         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6148         p = buf;\r
6149         ParseArgs(StringGet, &p);\r
6150       }\r
6151       EndDialog(hDlg, TRUE);\r
6152       return TRUE;\r
6153 \r
6154     case IDCANCEL:\r
6155       ExitEvent(0);\r
6156       return TRUE;\r
6157 \r
6158     case IDM_HELPCONTENTS:\r
6159       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6160         MessageBox (GetFocus(),\r
6161                     _("Unable to activate help"),\r
6162                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6163       }\r
6164       break;\r
6165 \r
6166     default:\r
6167       SetStartupDialogEnables(hDlg);\r
6168       break;\r
6169     }\r
6170     break;\r
6171   }\r
6172   return FALSE;\r
6173 }\r
6174 \r
6175 /*---------------------------------------------------------------------------*\\r
6176  *\r
6177  * About box dialog functions\r
6178  *\r
6179 \*---------------------------------------------------------------------------*/\r
6180 \r
6181 /* Process messages for "About" dialog box */\r
6182 LRESULT CALLBACK\r
6183 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6184 {\r
6185   switch (message) {\r
6186   case WM_INITDIALOG: /* message: initialize dialog box */\r
6187     /* Center the dialog over the application window */\r
6188     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6189     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6190     Translate(hDlg, ABOUTBOX);\r
6191     JAWS_COPYRIGHT\r
6192     return (TRUE);\r
6193 \r
6194   case WM_COMMAND: /* message: received a command */\r
6195     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6196         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6197       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6198       return (TRUE);\r
6199     }\r
6200     break;\r
6201   }\r
6202   return (FALSE);\r
6203 }\r
6204 \r
6205 /*---------------------------------------------------------------------------*\\r
6206  *\r
6207  * Comment Dialog functions\r
6208  *\r
6209 \*---------------------------------------------------------------------------*/\r
6210 \r
6211 LRESULT CALLBACK\r
6212 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6213 {\r
6214   static HANDLE hwndText = NULL;\r
6215   int len, newSizeX, newSizeY, flags;\r
6216   static int sizeX, sizeY;\r
6217   char *str;\r
6218   RECT rect;\r
6219   MINMAXINFO *mmi;\r
6220 \r
6221   switch (message) {\r
6222   case WM_INITDIALOG: /* message: initialize dialog box */\r
6223     /* Initialize the dialog items */\r
6224     Translate(hDlg, DLG_EditComment);\r
6225     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6226     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6227     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6228     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6229     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6230     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6231     SetWindowText(hDlg, commentTitle);\r
6232     if (editComment) {\r
6233       SetFocus(hwndText);\r
6234     } else {\r
6235       SetFocus(GetDlgItem(hDlg, IDOK));\r
6236     }\r
6237     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6238                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6239                 MAKELPARAM(FALSE, 0));\r
6240     /* Size and position the dialog */\r
6241     if (!commentDialog) {\r
6242       commentDialog = hDlg;\r
6243       flags = SWP_NOZORDER;\r
6244       GetClientRect(hDlg, &rect);\r
6245       sizeX = rect.right;\r
6246       sizeY = rect.bottom;\r
6247       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6248           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6249         WINDOWPLACEMENT wp;\r
6250         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6251         wp.length = sizeof(WINDOWPLACEMENT);\r
6252         wp.flags = 0;\r
6253         wp.showCmd = SW_SHOW;\r
6254         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6255         wp.rcNormalPosition.left = wpComment.x;\r
6256         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6257         wp.rcNormalPosition.top = wpComment.y;\r
6258         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6259         SetWindowPlacement(hDlg, &wp);\r
6260 \r
6261         GetClientRect(hDlg, &rect);\r
6262         newSizeX = rect.right;\r
6263         newSizeY = rect.bottom;\r
6264         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6265                               newSizeX, newSizeY);\r
6266         sizeX = newSizeX;\r
6267         sizeY = newSizeY;\r
6268       }\r
6269     }\r
6270     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6271     return FALSE;\r
6272 \r
6273   case WM_COMMAND: /* message: received a command */\r
6274     switch (LOWORD(wParam)) {\r
6275     case IDOK:\r
6276       if (editComment) {\r
6277         char *p, *q;\r
6278         /* Read changed options from the dialog box */\r
6279         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6280         len = GetWindowTextLength(hwndText);\r
6281         str = (char *) malloc(len + 1);\r
6282         GetWindowText(hwndText, str, len + 1);\r
6283         p = q = str;\r
6284         while (*q) {\r
6285           if (*q == '\r')\r
6286             q++;\r
6287           else\r
6288             *p++ = *q++;\r
6289         }\r
6290         *p = NULLCHAR;\r
6291         ReplaceComment(commentIndex, str);\r
6292         free(str);\r
6293       }\r
6294       CommentPopDown();\r
6295       return TRUE;\r
6296 \r
6297     case IDCANCEL:\r
6298     case OPT_CancelComment:\r
6299       CommentPopDown();\r
6300       return TRUE;\r
6301 \r
6302     case OPT_ClearComment:\r
6303       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6304       break;\r
6305 \r
6306     case OPT_EditComment:\r
6307       EditCommentEvent();\r
6308       return TRUE;\r
6309 \r
6310     default:\r
6311       break;\r
6312     }\r
6313     break;\r
6314 \r
6315   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6316         if( wParam == OPT_CommentText ) {\r
6317             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6318 \r
6319             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6320                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6321                 POINTL pt;\r
6322                 LRESULT index;\r
6323 \r
6324                 pt.x = LOWORD( lpMF->lParam );\r
6325                 pt.y = HIWORD( lpMF->lParam );\r
6326 \r
6327                 if(lpMF->msg == WM_CHAR) {\r
6328                         CHARRANGE sel;\r
6329                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6330                         index = sel.cpMin;\r
6331                 } else\r
6332                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6333 \r
6334                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6335                 len = GetWindowTextLength(hwndText);\r
6336                 str = (char *) malloc(len + 1);\r
6337                 GetWindowText(hwndText, str, len + 1);\r
6338                 ReplaceComment(commentIndex, str);\r
6339                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6340                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6341                 free(str);\r
6342 \r
6343                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6344                 lpMF->msg = WM_USER;\r
6345 \r
6346                 return TRUE;\r
6347             }\r
6348         }\r
6349         break;\r
6350 \r
6351   case WM_SIZE:\r
6352     newSizeX = LOWORD(lParam);\r
6353     newSizeY = HIWORD(lParam);\r
6354     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6355     sizeX = newSizeX;\r
6356     sizeY = newSizeY;\r
6357     break;\r
6358 \r
6359   case WM_GETMINMAXINFO:\r
6360     /* Prevent resizing window too small */\r
6361     mmi = (MINMAXINFO *) lParam;\r
6362     mmi->ptMinTrackSize.x = 100;\r
6363     mmi->ptMinTrackSize.y = 100;\r
6364     break;\r
6365   }\r
6366   return FALSE;\r
6367 }\r
6368 \r
6369 VOID\r
6370 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6371 {\r
6372   FARPROC lpProc;\r
6373   char *p, *q;\r
6374 \r
6375   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6376 \r
6377   if (str == NULL) str = "";\r
6378   p = (char *) malloc(2 * strlen(str) + 2);\r
6379   q = p;\r
6380   while (*str) {\r
6381     if (*str == '\n') *q++ = '\r';\r
6382     *q++ = *str++;\r
6383   }\r
6384   *q = NULLCHAR;\r
6385   if (commentText != NULL) free(commentText);\r
6386 \r
6387   commentIndex = index;\r
6388   commentTitle = title;\r
6389   commentText = p;\r
6390   editComment = edit;\r
6391 \r
6392   if (commentDialog) {\r
6393     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6394     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6395   } else {\r
6396     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6397     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6398                  hwndMain, (DLGPROC)lpProc);\r
6399     FreeProcInstance(lpProc);\r
6400   }\r
6401   commentUp = TRUE;\r
6402 }\r
6403 \r
6404 \r
6405 /*---------------------------------------------------------------------------*\\r
6406  *\r
6407  * Type-in move dialog functions\r
6408  * \r
6409 \*---------------------------------------------------------------------------*/\r
6410 \r
6411 LRESULT CALLBACK\r
6412 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6413 {\r
6414   char move[MSG_SIZ];\r
6415   HWND hInput;\r
6416 \r
6417   switch (message) {\r
6418   case WM_INITDIALOG:\r
6419     move[0] = (char) lParam;\r
6420     move[1] = NULLCHAR;\r
6421     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6422     Translate(hDlg, DLG_TypeInMove);\r
6423     hInput = GetDlgItem(hDlg, OPT_Move);\r
6424     SetWindowText(hInput, move);\r
6425     SetFocus(hInput);\r
6426     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6427     return FALSE;\r
6428 \r
6429   case WM_COMMAND:\r
6430     switch (LOWORD(wParam)) {\r
6431     case IDOK:
6432 \r
6433       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6434       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6435       TypeInDoneEvent(move);\r
6436       EndDialog(hDlg, TRUE);\r
6437       return TRUE;\r
6438     case IDCANCEL:\r
6439       EndDialog(hDlg, FALSE);\r
6440       return TRUE;\r
6441     default:\r
6442       break;\r
6443     }\r
6444     break;\r
6445   }\r
6446   return FALSE;\r
6447 }\r
6448 \r
6449 VOID\r
6450 PopUpMoveDialog(char firstchar)\r
6451 {\r
6452     FARPROC lpProc;\r
6453 \r
6454       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6455       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6456         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6457       FreeProcInstance(lpProc);\r
6458 }\r
6459 \r
6460 /*---------------------------------------------------------------------------*\\r
6461  *\r
6462  * Type-in name dialog functions\r
6463  * \r
6464 \*---------------------------------------------------------------------------*/\r
6465 \r
6466 LRESULT CALLBACK\r
6467 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6468 {\r
6469   char move[MSG_SIZ];\r
6470   HWND hInput;\r
6471 \r
6472   switch (message) {\r
6473   case WM_INITDIALOG:\r
6474     move[0] = (char) lParam;\r
6475     move[1] = NULLCHAR;\r
6476     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6477     Translate(hDlg, DLG_TypeInName);\r
6478     hInput = GetDlgItem(hDlg, OPT_Name);\r
6479     SetWindowText(hInput, move);\r
6480     SetFocus(hInput);\r
6481     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6482     return FALSE;\r
6483 \r
6484   case WM_COMMAND:\r
6485     switch (LOWORD(wParam)) {\r
6486     case IDOK:\r
6487       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6488       appData.userName = strdup(move);\r
6489       SetUserLogo();\r
6490       SetGameInfo();\r
6491       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6492         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6493         DisplayTitle(move);\r
6494       }\r
6495 \r
6496 \r
6497       EndDialog(hDlg, TRUE);\r
6498       return TRUE;\r
6499     case IDCANCEL:\r
6500       EndDialog(hDlg, FALSE);\r
6501       return TRUE;\r
6502     default:\r
6503       break;\r
6504     }\r
6505     break;\r
6506   }\r
6507   return FALSE;\r
6508 }\r
6509 \r
6510 VOID\r
6511 PopUpNameDialog(char firstchar)\r
6512 {\r
6513     FARPROC lpProc;\r
6514     \r
6515       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6516       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6517         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6518       FreeProcInstance(lpProc);\r
6519 }\r
6520 \r
6521 /*---------------------------------------------------------------------------*\\r
6522  *\r
6523  *  Error dialogs\r
6524  * \r
6525 \*---------------------------------------------------------------------------*/\r
6526 \r
6527 /* Nonmodal error box */\r
6528 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6529                              WPARAM wParam, LPARAM lParam);\r
6530 \r
6531 VOID\r
6532 ErrorPopUp(char *title, char *content)\r
6533 {\r
6534   FARPROC lpProc;\r
6535   char *p, *q;\r
6536   BOOLEAN modal = hwndMain == NULL;\r
6537 \r
6538   p = content;\r
6539   q = errorMessage;\r
6540   while (*p) {\r
6541     if (*p == '\n') {\r
6542       if (modal) {\r
6543         *q++ = ' ';\r
6544         p++;\r
6545       } else {\r
6546         *q++ = '\r';\r
6547         *q++ = *p++;\r
6548       }\r
6549     } else {\r
6550       *q++ = *p++;\r
6551     }\r
6552   }\r
6553   *q = NULLCHAR;\r
6554   strncpy(errorTitle, title, sizeof(errorTitle));\r
6555   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6556   \r
6557   if (modal) {\r
6558     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6559   } else {\r
6560     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6561     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6562                  hwndMain, (DLGPROC)lpProc);\r
6563     FreeProcInstance(lpProc);\r
6564   }\r
6565 }\r
6566 \r
6567 VOID\r
6568 ErrorPopDown()\r
6569 {\r
6570   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6571   if (errorDialog == NULL) return;\r
6572   DestroyWindow(errorDialog);\r
6573   errorDialog = NULL;\r
6574   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6575 }\r
6576 \r
6577 LRESULT CALLBACK\r
6578 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6579 {\r
6580   HANDLE hwndText;\r
6581   RECT rChild;\r
6582 \r
6583   switch (message) {\r
6584   case WM_INITDIALOG:\r
6585     GetWindowRect(hDlg, &rChild);\r
6586 \r
6587     /*\r
6588     SetWindowPos(hDlg, NULL, rChild.left,\r
6589       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6590       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6591     */\r
6592 \r
6593     /* \r
6594         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6595         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6596         and it doesn't work when you resize the dialog.\r
6597         For now, just give it a default position.\r
6598     */\r
6599     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6600     Translate(hDlg, DLG_Error);\r
6601 \r
6602     errorDialog = hDlg;\r
6603     SetWindowText(hDlg, errorTitle);\r
6604     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6605     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6606     return FALSE;\r
6607 \r
6608   case WM_COMMAND:\r
6609     switch (LOWORD(wParam)) {\r
6610     case IDOK:\r
6611     case IDCANCEL:\r
6612       if (errorDialog == hDlg) errorDialog = NULL;\r
6613       DestroyWindow(hDlg);\r
6614       return TRUE;\r
6615 \r
6616     default:\r
6617       break;\r
6618     }\r
6619     break;\r
6620   }\r
6621   return FALSE;\r
6622 }\r
6623 \r
6624 #ifdef GOTHIC\r
6625 HWND gothicDialog = NULL;\r
6626 \r
6627 LRESULT CALLBACK\r
6628 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6629 {\r
6630   HANDLE hwndText;\r
6631   RECT rChild;\r
6632   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6633 \r
6634   switch (message) {\r
6635   case WM_INITDIALOG:\r
6636     GetWindowRect(hDlg, &rChild);\r
6637 \r
6638     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6639                                                              SWP_NOZORDER);\r
6640 \r
6641     /* \r
6642         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6643         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6644         and it doesn't work when you resize the dialog.\r
6645         For now, just give it a default position.\r
6646     */\r
6647     gothicDialog = hDlg;\r
6648     SetWindowText(hDlg, errorTitle);\r
6649     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6650     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6651     return FALSE;\r
6652 \r
6653   case WM_COMMAND:\r
6654     switch (LOWORD(wParam)) {\r
6655     case IDOK:\r
6656     case IDCANCEL:\r
6657       if (errorDialog == hDlg) errorDialog = NULL;\r
6658       DestroyWindow(hDlg);\r
6659       return TRUE;\r
6660 \r
6661     default:\r
6662       break;\r
6663     }\r
6664     break;\r
6665   }\r
6666   return FALSE;\r
6667 }\r
6668 \r
6669 VOID\r
6670 GothicPopUp(char *title, VariantClass variant)\r
6671 {\r
6672   FARPROC lpProc;\r
6673   static char *lastTitle;\r
6674 \r
6675   strncpy(errorTitle, title, sizeof(errorTitle));\r
6676   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6677 \r
6678   if(lastTitle != title && gothicDialog != NULL) {\r
6679     DestroyWindow(gothicDialog);\r
6680     gothicDialog = NULL;\r
6681   }\r
6682   if(variant != VariantNormal && gothicDialog == NULL) {\r
6683     title = lastTitle;\r
6684     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6685     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6686                  hwndMain, (DLGPROC)lpProc);\r
6687     FreeProcInstance(lpProc);\r
6688   }\r
6689 }\r
6690 #endif\r
6691 \r
6692 /*---------------------------------------------------------------------------*\\r
6693  *\r
6694  *  Ics Interaction console functions\r
6695  *\r
6696 \*---------------------------------------------------------------------------*/\r
6697 \r
6698 #define HISTORY_SIZE 64\r
6699 static char *history[HISTORY_SIZE];\r
6700 int histIn = 0, histP = 0;\r
6701 \r
6702 VOID\r
6703 SaveInHistory(char *cmd)\r
6704 {\r
6705   if (history[histIn] != NULL) {\r
6706     free(history[histIn]);\r
6707     history[histIn] = NULL;\r
6708   }\r
6709   if (*cmd == NULLCHAR) return;\r
6710   history[histIn] = StrSave(cmd);\r
6711   histIn = (histIn + 1) % HISTORY_SIZE;\r
6712   if (history[histIn] != NULL) {\r
6713     free(history[histIn]);\r
6714     history[histIn] = NULL;\r
6715   }\r
6716   histP = histIn;\r
6717 }\r
6718 \r
6719 char *\r
6720 PrevInHistory(char *cmd)\r
6721 {\r
6722   int newhp;\r
6723   if (histP == histIn) {\r
6724     if (history[histIn] != NULL) free(history[histIn]);\r
6725     history[histIn] = StrSave(cmd);\r
6726   }\r
6727   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6728   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6729   histP = newhp;\r
6730   return history[histP];\r
6731 }\r
6732 \r
6733 char *\r
6734 NextInHistory()\r
6735 {\r
6736   if (histP == histIn) return NULL;\r
6737   histP = (histP + 1) % HISTORY_SIZE;\r
6738   return history[histP];   \r
6739 }\r
6740 \r
6741 HMENU\r
6742 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6743 {\r
6744   HMENU hmenu, h;\r
6745   int i = 0;\r
6746   hmenu = LoadMenu(hInst, "TextMenu");\r
6747   h = GetSubMenu(hmenu, 0);\r
6748   while (e->item) {\r
6749     if (strcmp(e->item, "-") == 0) {\r
6750       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6751     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6752       int flags = MF_STRING, j = 0;\r
6753       if (e->item[0] == '|') {\r
6754         flags |= MF_MENUBARBREAK;\r
6755         j++;\r
6756       }\r
6757       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6758       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6759     }\r
6760     e++;\r
6761     i++;\r
6762   } \r
6763   return hmenu;\r
6764 }\r
6765 \r
6766 WNDPROC consoleTextWindowProc;\r
6767 \r
6768 void\r
6769 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6770 {\r
6771   char buf[MSG_SIZ], name[MSG_SIZ];\r
6772   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6773   CHARRANGE sel;\r
6774 \r
6775   if (!getname) {\r
6776     SetWindowText(hInput, command);\r
6777     if (immediate) {\r
6778       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6779     } else {\r
6780       sel.cpMin = 999999;\r
6781       sel.cpMax = 999999;\r
6782       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6783       SetFocus(hInput);\r
6784     }\r
6785     return;\r
6786   }    \r
6787   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6788   if (sel.cpMin == sel.cpMax) {\r
6789     /* Expand to surrounding word */\r
6790     TEXTRANGE tr;\r
6791     do {\r
6792       tr.chrg.cpMax = sel.cpMin;\r
6793       tr.chrg.cpMin = --sel.cpMin;\r
6794       if (sel.cpMin < 0) break;\r
6795       tr.lpstrText = name;\r
6796       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6797     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6798     sel.cpMin++;\r
6799 \r
6800     do {\r
6801       tr.chrg.cpMin = sel.cpMax;\r
6802       tr.chrg.cpMax = ++sel.cpMax;\r
6803       tr.lpstrText = name;\r
6804       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6805     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6806     sel.cpMax--;\r
6807 \r
6808     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6809       MessageBeep(MB_ICONEXCLAMATION);\r
6810       return;\r
6811     }\r
6812     tr.chrg = sel;\r
6813     tr.lpstrText = name;\r
6814     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6815   } else {\r
6816     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6817       MessageBeep(MB_ICONEXCLAMATION);\r
6818       return;\r
6819     }\r
6820     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6821   }\r
6822   if (immediate) {\r
6823     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6824     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6825     SetWindowText(hInput, buf);\r
6826     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6827   } else {\r
6828     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6829       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6830     SetWindowText(hInput, buf);\r
6831     sel.cpMin = 999999;\r
6832     sel.cpMax = 999999;\r
6833     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6834     SetFocus(hInput);\r
6835   }\r
6836 }\r
6837 \r
6838 LRESULT CALLBACK \r
6839 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6840 {\r
6841   HWND hInput;\r
6842   CHARRANGE sel;\r
6843 \r
6844   switch (message) {\r
6845   case WM_KEYDOWN:\r
6846     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6847     if(wParam=='R') return 0;\r
6848     switch (wParam) {\r
6849     case VK_PRIOR:\r
6850       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6851       return 0;\r
6852     case VK_NEXT:\r
6853       sel.cpMin = 999999;\r
6854       sel.cpMax = 999999;\r
6855       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6856       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6857       return 0;\r
6858     }\r
6859     break;\r
6860   case WM_CHAR:\r
6861    if(wParam != '\022') {\r
6862     if (wParam == '\t') {\r
6863       if (GetKeyState(VK_SHIFT) < 0) {\r
6864         /* shifted */\r
6865         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6866         if (buttonDesc[0].hwnd) {\r
6867           SetFocus(buttonDesc[0].hwnd);\r
6868         } else {\r
6869           SetFocus(hwndMain);\r
6870         }\r
6871       } else {\r
6872         /* unshifted */\r
6873         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6874       }\r
6875     } else {\r
6876       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6877       JAWS_DELETE( SetFocus(hInput); )\r
6878       SendMessage(hInput, message, wParam, lParam);\r
6879     }\r
6880     return 0;\r
6881    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6882    lParam = -1;\r
6883   case WM_RBUTTONDOWN:\r
6884     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6885       /* Move selection here if it was empty */\r
6886       POINT pt;\r
6887       pt.x = LOWORD(lParam);\r
6888       pt.y = HIWORD(lParam);\r
6889       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6890       if (sel.cpMin == sel.cpMax) {\r
6891         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6892         sel.cpMax = sel.cpMin;\r
6893         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6894       }\r
6895       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6896 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6897       POINT pt;\r
6898       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6899       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6900       if (sel.cpMin == sel.cpMax) {\r
6901         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6902         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6903       }\r
6904       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6905         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6906       }\r
6907       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6908       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6909       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6910       MenuPopup(hwnd, pt, hmenu, -1);\r
6911 }\r
6912     }\r
6913     return 0;\r
6914   case WM_RBUTTONUP:\r
6915     if (GetKeyState(VK_SHIFT) & ~1) {\r
6916       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6917         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6918     }\r
6919     return 0;\r
6920   case WM_PASTE:\r
6921     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6922     SetFocus(hInput);\r
6923     return SendMessage(hInput, message, wParam, lParam);\r
6924   case WM_MBUTTONDOWN:\r
6925     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6926   case WM_COMMAND:\r
6927     switch (LOWORD(wParam)) {\r
6928     case IDM_QuickPaste:\r
6929       {\r
6930         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6931         if (sel.cpMin == sel.cpMax) {\r
6932           MessageBeep(MB_ICONEXCLAMATION);\r
6933           return 0;\r
6934         }\r
6935         SendMessage(hwnd, WM_COPY, 0, 0);\r
6936         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6937         SendMessage(hInput, WM_PASTE, 0, 0);\r
6938         SetFocus(hInput);\r
6939         return 0;\r
6940       }\r
6941     case IDM_Cut:\r
6942       SendMessage(hwnd, WM_CUT, 0, 0);\r
6943       return 0;\r
6944     case IDM_Paste:\r
6945       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6946       return 0;\r
6947     case IDM_Copy:\r
6948       SendMessage(hwnd, WM_COPY, 0, 0);\r
6949       return 0;\r
6950     default:\r
6951       {\r
6952         int i = LOWORD(wParam) - IDM_CommandX;\r
6953         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6954             icsTextMenuEntry[i].command != NULL) {\r
6955           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6956                    icsTextMenuEntry[i].getname,\r
6957                    icsTextMenuEntry[i].immediate);\r
6958           return 0;\r
6959         }\r
6960       }\r
6961       break;\r
6962     }\r
6963     break;\r
6964   }\r
6965   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6966 }\r
6967 \r
6968 WNDPROC consoleInputWindowProc;\r
6969 \r
6970 LRESULT CALLBACK\r
6971 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6972 {\r
6973   char buf[MSG_SIZ];\r
6974   char *p;\r
6975   static BOOL sendNextChar = FALSE;\r
6976   static BOOL quoteNextChar = FALSE;\r
6977   InputSource *is = consoleInputSource;\r
6978   CHARFORMAT cf;\r
6979   CHARRANGE sel;\r
6980 \r
6981   switch (message) {\r
6982   case WM_CHAR:\r
6983     if (!appData.localLineEditing || sendNextChar) {\r
6984       is->buf[0] = (CHAR) wParam;\r
6985       is->count = 1;\r
6986       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6987       sendNextChar = FALSE;\r
6988       return 0;\r
6989     }\r
6990     if (quoteNextChar) {\r
6991       buf[0] = (char) wParam;\r
6992       buf[1] = NULLCHAR;\r
6993       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6994       quoteNextChar = FALSE;\r
6995       return 0;\r
6996     }\r
6997     switch (wParam) {\r
6998     case '\r':   /* Enter key */\r
6999       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7000       if (consoleEcho) SaveInHistory(is->buf);\r
7001       is->buf[is->count++] = '\n';\r
7002       is->buf[is->count] = NULLCHAR;\r
7003       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7004       if (consoleEcho) {\r
7005         ConsoleOutput(is->buf, is->count, TRUE);\r
7006       } else if (appData.localLineEditing) {\r
7007         ConsoleOutput("\n", 1, TRUE);\r
7008       }\r
7009       /* fall thru */\r
7010     case '\033': /* Escape key */\r
7011       SetWindowText(hwnd, "");\r
7012       cf.cbSize = sizeof(CHARFORMAT);\r
7013       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7014       if (consoleEcho) {\r
7015         cf.crTextColor = textAttribs[ColorNormal].color;\r
7016       } else {\r
7017         cf.crTextColor = COLOR_ECHOOFF;\r
7018       }\r
7019       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7020       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7021       return 0;\r
7022     case '\t':   /* Tab key */\r
7023       if (GetKeyState(VK_SHIFT) < 0) {\r
7024         /* shifted */\r
7025         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7026       } else {\r
7027         /* unshifted */\r
7028         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7029         if (buttonDesc[0].hwnd) {\r
7030           SetFocus(buttonDesc[0].hwnd);\r
7031         } else {\r
7032           SetFocus(hwndMain);\r
7033         }\r
7034       }\r
7035       return 0;\r
7036     case '\023': /* Ctrl+S */\r
7037       sendNextChar = TRUE;\r
7038       return 0;\r
7039     case '\021': /* Ctrl+Q */\r
7040       quoteNextChar = TRUE;\r
7041       return 0;\r
7042     JAWS_REPLAY\r
7043     default:\r
7044       break;\r
7045     }\r
7046     break;\r
7047   case WM_KEYDOWN:\r
7048     switch (wParam) {\r
7049     case VK_UP:\r
7050       GetWindowText(hwnd, buf, MSG_SIZ);\r
7051       p = PrevInHistory(buf);\r
7052       if (p != NULL) {\r
7053         SetWindowText(hwnd, p);\r
7054         sel.cpMin = 999999;\r
7055         sel.cpMax = 999999;\r
7056         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7057         return 0;\r
7058       }\r
7059       break;\r
7060     case VK_DOWN:\r
7061       p = NextInHistory();\r
7062       if (p != NULL) {\r
7063         SetWindowText(hwnd, p);\r
7064         sel.cpMin = 999999;\r
7065         sel.cpMax = 999999;\r
7066         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7067         return 0;\r
7068       }\r
7069       break;\r
7070     case VK_HOME:\r
7071     case VK_END:\r
7072       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7073       /* fall thru */\r
7074     case VK_PRIOR:\r
7075     case VK_NEXT:\r
7076       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7077       return 0;\r
7078     }\r
7079     break;\r
7080   case WM_MBUTTONDOWN:\r
7081     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7082       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7083     break;\r
7084   case WM_RBUTTONUP:\r
7085     if (GetKeyState(VK_SHIFT) & ~1) {\r
7086       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7087         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7088     } else {\r
7089       POINT pt;\r
7090       HMENU hmenu;\r
7091       hmenu = LoadMenu(hInst, "InputMenu");\r
7092       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7093       if (sel.cpMin == sel.cpMax) {\r
7094         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7095         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7096       }\r
7097       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7098         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7099       }\r
7100       pt.x = LOWORD(lParam);\r
7101       pt.y = HIWORD(lParam);\r
7102       MenuPopup(hwnd, pt, hmenu, -1);\r
7103     }\r
7104     return 0;\r
7105   case WM_COMMAND:\r
7106     switch (LOWORD(wParam)) { \r
7107     case IDM_Undo:\r
7108       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7109       return 0;\r
7110     case IDM_SelectAll:\r
7111       sel.cpMin = 0;\r
7112       sel.cpMax = -1; /*999999?*/\r
7113       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7114       return 0;\r
7115     case IDM_Cut:\r
7116       SendMessage(hwnd, WM_CUT, 0, 0);\r
7117       return 0;\r
7118     case IDM_Paste:\r
7119       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7120       return 0;\r
7121     case IDM_Copy:\r
7122       SendMessage(hwnd, WM_COPY, 0, 0);\r
7123       return 0;\r
7124     }\r
7125     break;\r
7126   }\r
7127   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7128 }\r
7129 \r
7130 #define CO_MAX  100000\r
7131 #define CO_TRIM   1000\r
7132 \r
7133 LRESULT CALLBACK\r
7134 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7135 {\r
7136   static SnapData sd;\r
7137   HWND hText, hInput;\r
7138   RECT rect;\r
7139   static int sizeX, sizeY;\r
7140   int newSizeX, newSizeY;\r
7141   MINMAXINFO *mmi;\r
7142   WORD wMask;\r
7143 \r
7144   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7145   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7146 \r
7147   switch (message) {\r
7148   case WM_NOTIFY:\r
7149     if (((NMHDR*)lParam)->code == EN_LINK)\r
7150     {\r
7151       ENLINK *pLink = (ENLINK*)lParam;\r
7152       if (pLink->msg == WM_LBUTTONUP)\r
7153       {\r
7154         TEXTRANGE tr;\r
7155 \r
7156         tr.chrg = pLink->chrg;\r
7157         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7158         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7159         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7160         free(tr.lpstrText);\r
7161       }\r
7162     }\r
7163     break;\r
7164   case WM_INITDIALOG: /* message: initialize dialog box */\r
7165     hwndConsole = hDlg;\r
7166     SetFocus(hInput);\r
7167     consoleTextWindowProc = (WNDPROC)\r
7168       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7169     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7170     consoleInputWindowProc = (WNDPROC)\r
7171       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7172     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7173     Colorize(ColorNormal, TRUE);\r
7174     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7175     ChangedConsoleFont();\r
7176     GetClientRect(hDlg, &rect);\r
7177     sizeX = rect.right;\r
7178     sizeY = rect.bottom;\r
7179     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7180         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7181       WINDOWPLACEMENT wp;\r
7182       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7183       wp.length = sizeof(WINDOWPLACEMENT);\r
7184       wp.flags = 0;\r
7185       wp.showCmd = SW_SHOW;\r
7186       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7187       wp.rcNormalPosition.left = wpConsole.x;\r
7188       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7189       wp.rcNormalPosition.top = wpConsole.y;\r
7190       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7191       SetWindowPlacement(hDlg, &wp);\r
7192     }\r
7193 \r
7194    // [HGM] Chessknight's change 2004-07-13\r
7195    else { /* Determine Defaults */\r
7196        WINDOWPLACEMENT wp;\r
7197        wpConsole.x = wpMain.width + 1;\r
7198        wpConsole.y = wpMain.y;\r
7199        wpConsole.width = screenWidth -  wpMain.width;\r
7200        wpConsole.height = wpMain.height;\r
7201        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7202        wp.length = sizeof(WINDOWPLACEMENT);\r
7203        wp.flags = 0;\r
7204        wp.showCmd = SW_SHOW;\r
7205        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7206        wp.rcNormalPosition.left = wpConsole.x;\r
7207        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7208        wp.rcNormalPosition.top = wpConsole.y;\r
7209        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7210        SetWindowPlacement(hDlg, &wp);\r
7211     }\r
7212 \r
7213    // Allow hText to highlight URLs and send notifications on them\r
7214    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7215    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7216    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7217    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7218 \r
7219     return FALSE;\r
7220 \r
7221   case WM_SETFOCUS:\r
7222     SetFocus(hInput);\r
7223     return 0;\r
7224 \r
7225   case WM_CLOSE:\r
7226     ExitEvent(0);\r
7227     /* not reached */\r
7228     break;\r
7229 \r
7230   case WM_SIZE:\r
7231     if (IsIconic(hDlg)) break;\r
7232     newSizeX = LOWORD(lParam);\r
7233     newSizeY = HIWORD(lParam);\r
7234     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7235       RECT rectText, rectInput;\r
7236       POINT pt;\r
7237       int newTextHeight, newTextWidth;\r
7238       GetWindowRect(hText, &rectText);\r
7239       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7240       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7241       if (newTextHeight < 0) {\r
7242         newSizeY += -newTextHeight;\r
7243         newTextHeight = 0;\r
7244       }\r
7245       SetWindowPos(hText, NULL, 0, 0,\r
7246         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7247       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7248       pt.x = rectInput.left;\r
7249       pt.y = rectInput.top + newSizeY - sizeY;\r
7250       ScreenToClient(hDlg, &pt);\r
7251       SetWindowPos(hInput, NULL, \r
7252         pt.x, pt.y, /* needs client coords */   \r
7253         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7254         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7255     }\r
7256     sizeX = newSizeX;\r
7257     sizeY = newSizeY;\r
7258     break;\r
7259 \r
7260   case WM_GETMINMAXINFO:\r
7261     /* Prevent resizing window too small */\r
7262     mmi = (MINMAXINFO *) lParam;\r
7263     mmi->ptMinTrackSize.x = 100;\r
7264     mmi->ptMinTrackSize.y = 100;\r
7265     break;\r
7266 \r
7267   /* [AS] Snapping */\r
7268   case WM_ENTERSIZEMOVE:\r
7269     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7270 \r
7271   case WM_SIZING:\r
7272     return OnSizing( &sd, hDlg, wParam, lParam );\r
7273 \r
7274   case WM_MOVING:\r
7275     return OnMoving( &sd, hDlg, wParam, lParam );\r
7276 \r
7277   case WM_EXITSIZEMOVE:\r
7278         UpdateICSWidth(hText);\r
7279     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7280   }\r
7281 \r
7282   return DefWindowProc(hDlg, message, wParam, lParam);\r
7283 }\r
7284 \r
7285 \r
7286 VOID\r
7287 ConsoleCreate()\r
7288 {\r
7289   HWND hCons;\r
7290   if (hwndConsole) return;\r
7291   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7292   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7293 }\r
7294 \r
7295 \r
7296 VOID\r
7297 ConsoleOutput(char* data, int length, int forceVisible)\r
7298 {\r
7299   HWND hText;\r
7300   int trim, exlen;\r
7301   char *p, *q;\r
7302   char buf[CO_MAX+1];\r
7303   POINT pEnd;\r
7304   RECT rect;\r
7305   static int delayLF = 0;\r
7306   CHARRANGE savesel, sel;\r
7307 \r
7308   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7309   p = data;\r
7310   q = buf;\r
7311   if (delayLF) {\r
7312     *q++ = '\r';\r
7313     *q++ = '\n';\r
7314     delayLF = 0;\r
7315   }\r
7316   while (length--) {\r
7317     if (*p == '\n') {\r
7318       if (*++p) {\r
7319         *q++ = '\r';\r
7320         *q++ = '\n';\r
7321       } else {\r
7322         delayLF = 1;\r
7323       }\r
7324     } else if (*p == '\007') {\r
7325        MyPlaySound(&sounds[(int)SoundBell]);\r
7326        p++;\r
7327     } else {\r
7328       *q++ = *p++;\r
7329     }\r
7330   }\r
7331   *q = NULLCHAR;\r
7332   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7333   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7334   /* Save current selection */\r
7335   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7336   exlen = GetWindowTextLength(hText);\r
7337   /* Find out whether current end of text is visible */\r
7338   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7339   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7340   /* Trim existing text if it's too long */\r
7341   if (exlen + (q - buf) > CO_MAX) {\r
7342     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7343     sel.cpMin = 0;\r
7344     sel.cpMax = trim;\r
7345     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7346     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7347     exlen -= trim;\r
7348     savesel.cpMin -= trim;\r
7349     savesel.cpMax -= trim;\r
7350     if (exlen < 0) exlen = 0;\r
7351     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7352     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7353   }\r
7354   /* Append the new text */\r
7355   sel.cpMin = exlen;\r
7356   sel.cpMax = exlen;\r
7357   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7358   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7359   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7360   if (forceVisible || exlen == 0 ||\r
7361       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7362        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7363     /* Scroll to make new end of text visible if old end of text\r
7364        was visible or new text is an echo of user typein */\r
7365     sel.cpMin = 9999999;\r
7366     sel.cpMax = 9999999;\r
7367     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7368     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7369     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7370     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7371   }\r
7372   if (savesel.cpMax == exlen || forceVisible) {\r
7373     /* Move insert point to new end of text if it was at the old\r
7374        end of text or if the new text is an echo of user typein */\r
7375     sel.cpMin = 9999999;\r
7376     sel.cpMax = 9999999;\r
7377     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7378   } else {\r
7379     /* Restore previous selection */\r
7380     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7381   }\r
7382   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7383 }\r
7384 \r
7385 /*---------*/\r
7386 \r
7387 \r
7388 void\r
7389 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7390 {\r
7391   char buf[100];\r
7392   char *str;\r
7393   COLORREF oldFg, oldBg;\r
7394   HFONT oldFont;\r
7395   RECT rect;\r
7396 \r
7397   if(copyNumber > 1)
7398     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7399 \r
7400   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7401   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7402   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7403 \r
7404   rect.left = x;\r
7405   rect.right = x + squareSize;\r
7406   rect.top  = y;\r
7407   rect.bottom = y + squareSize;\r
7408   str = buf;\r
7409 \r
7410   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7411                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7412              y, ETO_CLIPPED|ETO_OPAQUE,\r
7413              &rect, str, strlen(str), NULL);\r
7414 \r
7415   (void) SetTextColor(hdc, oldFg);\r
7416   (void) SetBkColor(hdc, oldBg);\r
7417   (void) SelectObject(hdc, oldFont);\r
7418 }\r
7419 \r
7420 void\r
7421 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7422               RECT *rect, char *color, char *flagFell)\r
7423 {\r
7424   char buf[100];\r
7425   char *str;\r
7426   COLORREF oldFg, oldBg;\r
7427   HFONT oldFont;\r
7428 \r
7429   if (appData.clockMode) {\r
7430     if (tinyLayout)\r
7431       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7432     else\r
7433       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7434     str = buf;\r
7435   } else {\r
7436     str = color;\r
7437   }\r
7438 \r
7439   if (highlight) {\r
7440     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7441     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7442   } else {\r
7443     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7444     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7445   }\r
7446   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7447 \r
7448   JAWS_SILENCE\r
7449 \r
7450   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7451              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7452              rect, str, strlen(str), NULL);\r
7453   if(logoHeight > 0 && appData.clockMode) {\r
7454       RECT r;\r
7455       str += strlen(color)+2;\r
7456       r.top = rect->top + logoHeight/2;\r
7457       r.left = rect->left;\r
7458       r.right = rect->right;\r
7459       r.bottom = rect->bottom;\r
7460       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7461                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7462                  &r, str, strlen(str), NULL);\r
7463   }\r
7464   (void) SetTextColor(hdc, oldFg);\r
7465   (void) SetBkColor(hdc, oldBg);\r
7466   (void) SelectObject(hdc, oldFont);\r
7467 }\r
7468 \r
7469 \r
7470 int\r
7471 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7472            OVERLAPPED *ovl)\r
7473 {\r
7474   int ok, err;\r
7475 \r
7476   /* [AS]  */\r
7477   if( count <= 0 ) {\r
7478     if (appData.debugMode) {\r
7479       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7480     }\r
7481 \r
7482     return ERROR_INVALID_USER_BUFFER;\r
7483   }\r
7484 \r
7485   ResetEvent(ovl->hEvent);\r
7486   ovl->Offset = ovl->OffsetHigh = 0;\r
7487   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7488   if (ok) {\r
7489     err = NO_ERROR;\r
7490   } else {\r
7491     err = GetLastError();\r
7492     if (err == ERROR_IO_PENDING) {\r
7493       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7494       if (ok)\r
7495         err = NO_ERROR;\r
7496       else\r
7497         err = GetLastError();\r
7498     }\r
7499   }\r
7500   return err;\r
7501 }\r
7502 \r
7503 int\r
7504 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7505             OVERLAPPED *ovl)\r
7506 {\r
7507   int ok, err;\r
7508 \r
7509   ResetEvent(ovl->hEvent);\r
7510   ovl->Offset = ovl->OffsetHigh = 0;\r
7511   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7512   if (ok) {\r
7513     err = NO_ERROR;\r
7514   } else {\r
7515     err = GetLastError();\r
7516     if (err == ERROR_IO_PENDING) {\r
7517       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7518       if (ok)\r
7519         err = NO_ERROR;\r
7520       else\r
7521         err = GetLastError();\r
7522     }\r
7523   }\r
7524   return err;\r
7525 }\r
7526 \r
7527 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7528 void CheckForInputBufferFull( InputSource * is )\r
7529 {\r
7530     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7531         /* Look for end of line */\r
7532         char * p = is->buf;\r
7533         \r
7534         while( p < is->next && *p != '\n' ) {\r
7535             p++;\r
7536         }\r
7537 \r
7538         if( p >= is->next ) {\r
7539             if (appData.debugMode) {\r
7540                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7541             }\r
7542 \r
7543             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7544             is->count = (DWORD) -1;\r
7545             is->next = is->buf;\r
7546         }\r
7547     }\r
7548 }\r
7549 \r
7550 DWORD\r
7551 InputThread(LPVOID arg)\r
7552 {\r
7553   InputSource *is;\r
7554   OVERLAPPED ovl;\r
7555 \r
7556   is = (InputSource *) arg;\r
7557   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7558   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7559   while (is->hThread != NULL) {\r
7560     is->error = DoReadFile(is->hFile, is->next,\r
7561                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7562                            &is->count, &ovl);\r
7563     if (is->error == NO_ERROR) {\r
7564       is->next += is->count;\r
7565     } else {\r
7566       if (is->error == ERROR_BROKEN_PIPE) {\r
7567         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7568         is->count = 0;\r
7569       } else {\r
7570         is->count = (DWORD) -1;\r
7571         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7572         break; \r
7573       }\r
7574     }\r
7575 \r
7576     CheckForInputBufferFull( is );\r
7577 \r
7578     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7579 \r
7580     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7581 \r
7582     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7583   }\r
7584 \r
7585   CloseHandle(ovl.hEvent);\r
7586   CloseHandle(is->hFile);\r
7587 \r
7588   if (appData.debugMode) {\r
7589     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7590   }\r
7591 \r
7592   return 0;\r
7593 }\r
7594 \r
7595 \r
7596 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7597 DWORD\r
7598 NonOvlInputThread(LPVOID arg)\r
7599 {\r
7600   InputSource *is;\r
7601   char *p, *q;\r
7602   int i;\r
7603   char prev;\r
7604 \r
7605   is = (InputSource *) arg;\r
7606   while (is->hThread != NULL) {\r
7607     is->error = ReadFile(is->hFile, is->next,\r
7608                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7609                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7610     if (is->error == NO_ERROR) {\r
7611       /* Change CRLF to LF */\r
7612       if (is->next > is->buf) {\r
7613         p = is->next - 1;\r
7614         i = is->count + 1;\r
7615       } else {\r
7616         p = is->next;\r
7617         i = is->count;\r
7618       }\r
7619       q = p;\r
7620       prev = NULLCHAR;\r
7621       while (i > 0) {\r
7622         if (prev == '\r' && *p == '\n') {\r
7623           *(q-1) = '\n';\r
7624           is->count--;\r
7625         } else { \r
7626           *q++ = *p;\r
7627         }\r
7628         prev = *p++;\r
7629         i--;\r
7630       }\r
7631       *q = NULLCHAR;\r
7632       is->next = q;\r
7633     } else {\r
7634       if (is->error == ERROR_BROKEN_PIPE) {\r
7635         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7636         is->count = 0; \r
7637       } else {\r
7638         is->count = (DWORD) -1;\r
7639       }\r
7640     }\r
7641 \r
7642     CheckForInputBufferFull( is );\r
7643 \r
7644     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7645 \r
7646     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7647 \r
7648     if (is->count < 0) break;  /* Quit on error */\r
7649   }\r
7650   CloseHandle(is->hFile);\r
7651   return 0;\r
7652 }\r
7653 \r
7654 DWORD\r
7655 SocketInputThread(LPVOID arg)\r
7656 {\r
7657   InputSource *is;\r
7658 \r
7659   is = (InputSource *) arg;\r
7660   while (is->hThread != NULL) {\r
7661     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7662     if ((int)is->count == SOCKET_ERROR) {\r
7663       is->count = (DWORD) -1;\r
7664       is->error = WSAGetLastError();\r
7665     } else {\r
7666       is->error = NO_ERROR;\r
7667       is->next += is->count;\r
7668       if (is->count == 0 && is->second == is) {\r
7669         /* End of file on stderr; quit with no message */\r
7670         break;\r
7671       }\r
7672     }\r
7673     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7674 \r
7675     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7676 \r
7677     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7678   }\r
7679   return 0;\r
7680 }\r
7681 \r
7682 VOID\r
7683 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7684 {\r
7685   InputSource *is;\r
7686 \r
7687   is = (InputSource *) lParam;\r
7688   if (is->lineByLine) {\r
7689     /* Feed in lines one by one */\r
7690     char *p = is->buf;\r
7691     char *q = p;\r
7692     while (q < is->next) {\r
7693       if (*q++ == '\n') {\r
7694         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7695         p = q;\r
7696       }\r
7697     }\r
7698     \r
7699     /* Move any partial line to the start of the buffer */\r
7700     q = is->buf;\r
7701     while (p < is->next) {\r
7702       *q++ = *p++;\r
7703     }\r
7704     is->next = q;\r
7705 \r
7706     if (is->error != NO_ERROR || is->count == 0) {\r
7707       /* Notify backend of the error.  Note: If there was a partial\r
7708          line at the end, it is not flushed through. */\r
7709       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7710     }\r
7711   } else {\r
7712     /* Feed in the whole chunk of input at once */\r
7713     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7714     is->next = is->buf;\r
7715   }\r
7716 }\r
7717 \r
7718 /*---------------------------------------------------------------------------*\\r
7719  *\r
7720  *  Menu enables. Used when setting various modes.\r
7721  *\r
7722 \*---------------------------------------------------------------------------*/\r
7723 \r
7724 typedef struct {\r
7725   int item;\r
7726   int flags;\r
7727 } Enables;\r
7728 \r
7729 VOID\r
7730 GreyRevert(Boolean grey)\r
7731 { // [HGM] vari: for retracting variations in local mode\r
7732   HMENU hmenu = GetMenu(hwndMain);\r
7733   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7734   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7735 }\r
7736 \r
7737 VOID\r
7738 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7739 {\r
7740   while (enab->item > 0) {\r
7741     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7742     enab++;\r
7743   }\r
7744 }\r
7745 \r
7746 Enables gnuEnables[] = {\r
7747   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7751   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7753   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7754   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7755   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7756   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7757   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7760 \r
7761   // Needed to switch from ncp to GNU mode on Engine Load\r
7762   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7763   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7764   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7765   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7766   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7767   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7768   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7769   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7770   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7771   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7772   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7773   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7774   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7775   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7776   { -1, -1 }\r
7777 };\r
7778 \r
7779 Enables icsEnables[] = {\r
7780   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7788   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7795   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7799   { -1, -1 }\r
7800 };\r
7801 \r
7802 #if ZIPPY\r
7803 Enables zippyEnables[] = {\r
7804   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7805   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7806   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7807   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7808   { -1, -1 }\r
7809 };\r
7810 #endif\r
7811 \r
7812 Enables ncpEnables[] = {\r
7813   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7822   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7823   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7835   { -1, -1 }\r
7836 };\r
7837 \r
7838 Enables trainingOnEnables[] = {\r
7839   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7848   { -1, -1 }\r
7849 };\r
7850 \r
7851 Enables trainingOffEnables[] = {\r
7852   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7853   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7854   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7861   { -1, -1 }\r
7862 };\r
7863 \r
7864 /* These modify either ncpEnables or gnuEnables */\r
7865 Enables cmailEnables[] = {\r
7866   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7868   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7869   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7870   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7871   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7873   { -1, -1 }\r
7874 };\r
7875 \r
7876 Enables machineThinkingEnables[] = {\r
7877   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7890 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7893   { -1, -1 }\r
7894 };\r
7895 \r
7896 Enables userThinkingEnables[] = {\r
7897   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7898   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7899   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7900   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7901   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7910 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7913   { -1, -1 }\r
7914 };\r
7915 \r
7916 /*---------------------------------------------------------------------------*\\r
7917  *\r
7918  *  Front-end interface functions exported by XBoard.\r
7919  *  Functions appear in same order as prototypes in frontend.h.\r
7920  * \r
7921 \*---------------------------------------------------------------------------*/\r
7922 VOID\r
7923 CheckMark(UINT item, int state)\r
7924 {\r
7925     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7926 }\r
7927 \r
7928 VOID\r
7929 ModeHighlight()\r
7930 {\r
7931   static UINT prevChecked = 0;\r
7932   static int prevPausing = 0;\r
7933   UINT nowChecked;\r
7934 \r
7935   if (pausing != prevPausing) {\r
7936     prevPausing = pausing;\r
7937     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7938                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7939     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7940   }\r
7941 \r
7942   switch (gameMode) {\r
7943   case BeginningOfGame:\r
7944     if (appData.icsActive)\r
7945       nowChecked = IDM_IcsClient;\r
7946     else if (appData.noChessProgram)\r
7947       nowChecked = IDM_EditGame;\r
7948     else\r
7949       nowChecked = IDM_MachineBlack;\r
7950     break;\r
7951   case MachinePlaysBlack:\r
7952     nowChecked = IDM_MachineBlack;\r
7953     break;\r
7954   case MachinePlaysWhite:\r
7955     nowChecked = IDM_MachineWhite;\r
7956     break;\r
7957   case TwoMachinesPlay:\r
7958     nowChecked = IDM_TwoMachines;\r
7959     break;\r
7960   case AnalyzeMode:\r
7961     nowChecked = IDM_AnalysisMode;\r
7962     break;\r
7963   case AnalyzeFile:\r
7964     nowChecked = IDM_AnalyzeFile;\r
7965     break;\r
7966   case EditGame:\r
7967     nowChecked = IDM_EditGame;\r
7968     break;\r
7969   case PlayFromGameFile:\r
7970     nowChecked = IDM_LoadGame;\r
7971     break;\r
7972   case EditPosition:\r
7973     nowChecked = IDM_EditPosition;\r
7974     break;\r
7975   case Training:\r
7976     nowChecked = IDM_Training;\r
7977     break;\r
7978   case IcsPlayingWhite:\r
7979   case IcsPlayingBlack:\r
7980   case IcsObserving:\r
7981   case IcsIdle:\r
7982     nowChecked = IDM_IcsClient;\r
7983     break;\r
7984   default:\r
7985   case EndOfGame:\r
7986     nowChecked = 0;\r
7987     break;\r
7988   }\r
7989   CheckMark(prevChecked, MF_UNCHECKED);\r
7990   CheckMark(nowChecked, MF_CHECKED);\r
7991   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
7992 \r
7993   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7994     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7995                           MF_BYCOMMAND|MF_ENABLED);\r
7996   } else {\r
7997     (void) EnableMenuItem(GetMenu(hwndMain), \r
7998                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7999   }\r
8000 \r
8001   prevChecked = nowChecked;\r
8002 \r
8003   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8004   if (appData.icsActive) {\r
8005        if (appData.icsEngineAnalyze) {\r
8006                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8007        } else {\r
8008                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8009        }\r
8010   }\r
8011   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8012 }\r
8013 \r
8014 VOID\r
8015 SetICSMode()\r
8016 {\r
8017   HMENU hmenu = GetMenu(hwndMain);\r
8018   SetMenuEnables(hmenu, icsEnables);\r
8019   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8020     MF_BYCOMMAND|MF_ENABLED);\r
8021 #if ZIPPY\r
8022   if (appData.zippyPlay) {\r
8023     SetMenuEnables(hmenu, zippyEnables);\r
8024     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8025          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8026           MF_BYCOMMAND|MF_ENABLED);\r
8027   }\r
8028 #endif\r
8029 }\r
8030 \r
8031 VOID\r
8032 SetGNUMode()\r
8033 {\r
8034   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8035 }\r
8036 \r
8037 VOID\r
8038 SetNCPMode()\r
8039 {\r
8040   HMENU hmenu = GetMenu(hwndMain);\r
8041   SetMenuEnables(hmenu, ncpEnables);\r
8042     DrawMenuBar(hwndMain);\r
8043 }\r
8044 \r
8045 VOID\r
8046 SetCmailMode()\r
8047 {\r
8048   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8049 }\r
8050 \r
8051 VOID \r
8052 SetTrainingModeOn()\r
8053 {\r
8054   int i;\r
8055   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8056   for (i = 0; i < N_BUTTONS; i++) {\r
8057     if (buttonDesc[i].hwnd != NULL)\r
8058       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8059   }\r
8060   CommentPopDown();\r
8061 }\r
8062 \r
8063 VOID SetTrainingModeOff()\r
8064 {\r
8065   int i;\r
8066   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8067   for (i = 0; i < N_BUTTONS; i++) {\r
8068     if (buttonDesc[i].hwnd != NULL)\r
8069       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8070   }\r
8071 }\r
8072 \r
8073 \r
8074 VOID\r
8075 SetUserThinkingEnables()\r
8076 {\r
8077   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8078 }\r
8079 \r
8080 VOID\r
8081 SetMachineThinkingEnables()\r
8082 {\r
8083   HMENU hMenu = GetMenu(hwndMain);\r
8084   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8085 \r
8086   SetMenuEnables(hMenu, machineThinkingEnables);\r
8087 \r
8088   if (gameMode == MachinePlaysBlack) {\r
8089     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8090   } else if (gameMode == MachinePlaysWhite) {\r
8091     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8092   } else if (gameMode == TwoMachinesPlay) {\r
8093     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8094   }\r
8095 }\r
8096 \r
8097 \r
8098 VOID\r
8099 DisplayTitle(char *str)\r
8100 {\r
8101   char title[MSG_SIZ], *host;\r
8102   if (str[0] != NULLCHAR) {\r
8103     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8104   } else if (appData.icsActive) {\r
8105     if (appData.icsCommPort[0] != NULLCHAR)\r
8106       host = "ICS";\r
8107     else \r
8108       host = appData.icsHost;\r
8109       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8110   } else if (appData.noChessProgram) {\r
8111     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8112   } else {\r
8113     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8114     strcat(title, ": ");\r
8115     strcat(title, first.tidy);\r
8116   }\r
8117   SetWindowText(hwndMain, title);\r
8118 }\r
8119 \r
8120 \r
8121 VOID\r
8122 DisplayMessage(char *str1, char *str2)\r
8123 {\r
8124   HDC hdc;\r
8125   HFONT oldFont;\r
8126   int remain = MESSAGE_TEXT_MAX - 1;\r
8127   int len;\r
8128 \r
8129   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8130   messageText[0] = NULLCHAR;\r
8131   if (*str1) {\r
8132     len = strlen(str1);\r
8133     if (len > remain) len = remain;\r
8134     strncpy(messageText, str1, len);\r
8135     messageText[len] = NULLCHAR;\r
8136     remain -= len;\r
8137   }\r
8138   if (*str2 && remain >= 2) {\r
8139     if (*str1) {\r
8140       strcat(messageText, "  ");\r
8141       remain -= 2;\r
8142     }\r
8143     len = strlen(str2);\r
8144     if (len > remain) len = remain;\r
8145     strncat(messageText, str2, len);\r
8146   }\r
8147   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8148   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8149 \r
8150   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8151 \r
8152   SAYMACHINEMOVE();\r
8153 \r
8154   hdc = GetDC(hwndMain);\r
8155   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8156   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8157              &messageRect, messageText, strlen(messageText), NULL);\r
8158   (void) SelectObject(hdc, oldFont);\r
8159   (void) ReleaseDC(hwndMain, hdc);\r
8160 }\r
8161 \r
8162 VOID\r
8163 DisplayError(char *str, int error)\r
8164 {\r
8165   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8166   int len;\r
8167 \r
8168   if (error == 0) {\r
8169     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8170   } else {\r
8171     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8172                         NULL, error, LANG_NEUTRAL,\r
8173                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8174     if (len > 0) {\r
8175       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8176     } else {\r
8177       ErrorMap *em = errmap;\r
8178       while (em->err != 0 && em->err != error) em++;\r
8179       if (em->err != 0) {\r
8180         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8181       } else {\r
8182         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8183       }\r
8184     }\r
8185   }\r
8186   \r
8187   ErrorPopUp(_("Error"), buf);\r
8188 }\r
8189 \r
8190 \r
8191 VOID\r
8192 DisplayMoveError(char *str)\r
8193 {\r
8194   fromX = fromY = -1;\r
8195   ClearHighlights();\r
8196   DrawPosition(FALSE, NULL);\r
8197   if (appData.popupMoveErrors) {\r
8198     ErrorPopUp(_("Error"), str);\r
8199   } else {\r
8200     DisplayMessage(str, "");\r
8201     moveErrorMessageUp = TRUE;\r
8202   }\r
8203 }\r
8204 \r
8205 VOID\r
8206 DisplayFatalError(char *str, int error, int exitStatus)\r
8207 {\r
8208   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8209   int len;\r
8210   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8211 \r
8212   if (error != 0) {\r
8213     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8214                         NULL, error, LANG_NEUTRAL,\r
8215                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8216     if (len > 0) {\r
8217       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8218     } else {\r
8219       ErrorMap *em = errmap;\r
8220       while (em->err != 0 && em->err != error) em++;\r
8221       if (em->err != 0) {\r
8222         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8223       } else {\r
8224         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8225       }\r
8226     }\r
8227     str = buf;\r
8228   }\r
8229   if (appData.debugMode) {\r
8230     fprintf(debugFP, "%s: %s\n", label, str);\r
8231   }\r
8232   if (appData.popupExitMessage) {\r
8233     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8234                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8235   }\r
8236   ExitEvent(exitStatus);\r
8237 }\r
8238 \r
8239 \r
8240 VOID\r
8241 DisplayInformation(char *str)\r
8242 {\r
8243   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8244 }\r
8245 \r
8246 \r
8247 VOID\r
8248 DisplayNote(char *str)\r
8249 {\r
8250   ErrorPopUp(_("Note"), str);\r
8251 }\r
8252 \r
8253 \r
8254 typedef struct {\r
8255   char *title, *question, *replyPrefix;\r
8256   ProcRef pr;\r
8257 } QuestionParams;\r
8258 \r
8259 LRESULT CALLBACK\r
8260 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8261 {\r
8262   static QuestionParams *qp;\r
8263   char reply[MSG_SIZ];\r
8264   int len, err;\r
8265 \r
8266   switch (message) {\r
8267   case WM_INITDIALOG:\r
8268     qp = (QuestionParams *) lParam;\r
8269     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8270     Translate(hDlg, DLG_Question);\r
8271     SetWindowText(hDlg, qp->title);\r
8272     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8273     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8274     return FALSE;\r
8275 \r
8276   case WM_COMMAND:\r
8277     switch (LOWORD(wParam)) {\r
8278     case IDOK:\r
8279       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8280       if (*reply) strcat(reply, " ");\r
8281       len = strlen(reply);\r
8282       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8283       strcat(reply, "\n");\r
8284       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8285       EndDialog(hDlg, TRUE);\r
8286       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8287       return TRUE;\r
8288     case IDCANCEL:\r
8289       EndDialog(hDlg, FALSE);\r
8290       return TRUE;\r
8291     default:\r
8292       break;\r
8293     }\r
8294     break;\r
8295   }\r
8296   return FALSE;\r
8297 }\r
8298 \r
8299 VOID\r
8300 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8301 {\r
8302     QuestionParams qp;\r
8303     FARPROC lpProc;\r
8304     \r
8305     qp.title = title;\r
8306     qp.question = question;\r
8307     qp.replyPrefix = replyPrefix;\r
8308     qp.pr = pr;\r
8309     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8310     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8311       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8312     FreeProcInstance(lpProc);\r
8313 }\r
8314 \r
8315 /* [AS] Pick FRC position */\r
8316 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8317 {\r
8318     static int * lpIndexFRC;\r
8319     BOOL index_is_ok;\r
8320     char buf[16];\r
8321 \r
8322     switch( message )\r
8323     {\r
8324     case WM_INITDIALOG:\r
8325         lpIndexFRC = (int *) lParam;\r
8326 \r
8327         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8328         Translate(hDlg, DLG_NewGameFRC);\r
8329 \r
8330         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8331         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8332         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8333         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8334 \r
8335         break;\r
8336 \r
8337     case WM_COMMAND:\r
8338         switch( LOWORD(wParam) ) {\r
8339         case IDOK:\r
8340             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8341             EndDialog( hDlg, 0 );\r
8342             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8343             return TRUE;\r
8344         case IDCANCEL:\r
8345             EndDialog( hDlg, 1 );   \r
8346             return TRUE;\r
8347         case IDC_NFG_Edit:\r
8348             if( HIWORD(wParam) == EN_CHANGE ) {\r
8349                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8350 \r
8351                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8352             }\r
8353             return TRUE;\r
8354         case IDC_NFG_Random:\r
8355           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8356             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8357             return TRUE;\r
8358         }\r
8359 \r
8360         break;\r
8361     }\r
8362 \r
8363     return FALSE;\r
8364 }\r
8365 \r
8366 int NewGameFRC()\r
8367 {\r
8368     int result;\r
8369     int index = appData.defaultFrcPosition;\r
8370     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8371 \r
8372     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8373 \r
8374     if( result == 0 ) {\r
8375         appData.defaultFrcPosition = index;\r
8376     }\r
8377 \r
8378     return result;\r
8379 }\r
8380 \r
8381 /* [AS] Game list options. Refactored by HGM */\r
8382 \r
8383 HWND gameListOptionsDialog;\r
8384 \r
8385 // low-level front-end: clear text edit / list widget\r
8386 void\r
8387 GLT_ClearList()\r
8388 {\r
8389     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8390 }\r
8391 \r
8392 // low-level front-end: clear text edit / list widget\r
8393 void\r
8394 GLT_DeSelectList()\r
8395 {\r
8396     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8397 }\r
8398 \r
8399 // low-level front-end: append line to text edit / list widget\r
8400 void\r
8401 GLT_AddToList( char *name )\r
8402 {\r
8403     if( name != 0 ) {\r
8404             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8405     }\r
8406 }\r
8407 \r
8408 // low-level front-end: get line from text edit / list widget\r
8409 Boolean\r
8410 GLT_GetFromList( int index, char *name )\r
8411 {\r
8412     if( name != 0 ) {\r
8413             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8414                 return TRUE;\r
8415     }\r
8416     return FALSE;\r
8417 }\r
8418 \r
8419 void GLT_MoveSelection( HWND hDlg, int delta )\r
8420 {\r
8421     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8422     int idx2 = idx1 + delta;\r
8423     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8424 \r
8425     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8426         char buf[128];\r
8427 \r
8428         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8429         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8430         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8431         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8432     }\r
8433 }\r
8434 \r
8435 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8436 {\r
8437     switch( message )\r
8438     {\r
8439     case WM_INITDIALOG:\r
8440         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8441         \r
8442         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8443         Translate(hDlg, DLG_GameListOptions);\r
8444 \r
8445         /* Initialize list */\r
8446         GLT_TagsToList( lpUserGLT );\r
8447 \r
8448         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8449 \r
8450         break;\r
8451 \r
8452     case WM_COMMAND:\r
8453         switch( LOWORD(wParam) ) {\r
8454         case IDOK:\r
8455             GLT_ParseList();\r
8456             EndDialog( hDlg, 0 );\r
8457             return TRUE;\r
8458         case IDCANCEL:\r
8459             EndDialog( hDlg, 1 );\r
8460             return TRUE;\r
8461 \r
8462         case IDC_GLT_Default:\r
8463             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8464             return TRUE;\r
8465 \r
8466         case IDC_GLT_Restore:\r
8467             GLT_TagsToList( appData.gameListTags );\r
8468             return TRUE;\r
8469 \r
8470         case IDC_GLT_Up:\r
8471             GLT_MoveSelection( hDlg, -1 );\r
8472             return TRUE;\r
8473 \r
8474         case IDC_GLT_Down:\r
8475             GLT_MoveSelection( hDlg, +1 );\r
8476             return TRUE;\r
8477         }\r
8478 \r
8479         break;\r
8480     }\r
8481 \r
8482     return FALSE;\r
8483 }\r
8484 \r
8485 int GameListOptions()\r
8486 {\r
8487     int result;\r
8488     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8489 \r
8490       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8491 \r
8492     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8493 \r
8494     if( result == 0 ) {\r
8495         /* [AS] Memory leak here! */\r
8496         appData.gameListTags = strdup( lpUserGLT ); \r
8497     }\r
8498 \r
8499     return result;\r
8500 }\r
8501 \r
8502 VOID\r
8503 DisplayIcsInteractionTitle(char *str)\r
8504 {\r
8505   char consoleTitle[MSG_SIZ];\r
8506 \r
8507     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8508   SetWindowText(hwndConsole, consoleTitle);\r
8509 }\r
8510 \r
8511 void\r
8512 DrawPosition(int fullRedraw, Board board)\r
8513 {\r
8514   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8515 }\r
8516 \r
8517 void NotifyFrontendLogin()\r
8518 {\r
8519         if (hwndConsole)\r
8520                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8521 }\r
8522 \r
8523 VOID\r
8524 ResetFrontEnd()\r
8525 {\r
8526   fromX = fromY = -1;\r
8527   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8528     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8529     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8530     dragInfo.lastpos = dragInfo.pos;\r
8531     dragInfo.start.x = dragInfo.start.y = -1;\r
8532     dragInfo.from = dragInfo.start;\r
8533     ReleaseCapture();\r
8534     DrawPosition(TRUE, NULL);\r
8535   }\r
8536   TagsPopDown();\r
8537 }\r
8538 \r
8539 \r
8540 VOID\r
8541 CommentPopUp(char *title, char *str)\r
8542 {\r
8543   HWND hwnd = GetActiveWindow();\r
8544   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8545   SAY(str);\r
8546   SetActiveWindow(hwnd);\r
8547 }\r
8548 \r
8549 VOID\r
8550 CommentPopDown(void)\r
8551 {\r
8552   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8553   if (commentDialog) {\r
8554     ShowWindow(commentDialog, SW_HIDE);\r
8555   }\r
8556   commentUp = FALSE;\r
8557 }\r
8558 \r
8559 VOID\r
8560 EditCommentPopUp(int index, char *title, char *str)\r
8561 {\r
8562   EitherCommentPopUp(index, title, str, TRUE);\r
8563 }\r
8564 \r
8565 \r
8566 VOID\r
8567 RingBell()\r
8568 {\r
8569   MyPlaySound(&sounds[(int)SoundMove]);\r
8570 }\r
8571 \r
8572 VOID PlayIcsWinSound()\r
8573 {\r
8574   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8575 }\r
8576 \r
8577 VOID PlayIcsLossSound()\r
8578 {\r
8579   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8580 }\r
8581 \r
8582 VOID PlayIcsDrawSound()\r
8583 {\r
8584   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8585 }\r
8586 \r
8587 VOID PlayIcsUnfinishedSound()\r
8588 {\r
8589   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8590 }\r
8591 \r
8592 VOID\r
8593 PlayAlarmSound()\r
8594 {\r
8595   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8596 }\r
8597 \r
8598 \r
8599 VOID\r
8600 EchoOn()\r
8601 {\r
8602   HWND hInput;\r
8603   consoleEcho = TRUE;\r
8604   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8605   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8606   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8607 }\r
8608 \r
8609 \r
8610 VOID\r
8611 EchoOff()\r
8612 {\r
8613   CHARFORMAT cf;\r
8614   HWND hInput;\r
8615   consoleEcho = FALSE;\r
8616   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8617   /* This works OK: set text and background both to the same color */\r
8618   cf = consoleCF;\r
8619   cf.crTextColor = COLOR_ECHOOFF;\r
8620   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8621   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8622 }\r
8623 \r
8624 /* No Raw()...? */\r
8625 \r
8626 void Colorize(ColorClass cc, int continuation)\r
8627 {\r
8628   currentColorClass = cc;\r
8629   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8630   consoleCF.crTextColor = textAttribs[cc].color;\r
8631   consoleCF.dwEffects = textAttribs[cc].effects;\r
8632   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8633 }\r
8634 \r
8635 char *\r
8636 UserName()\r
8637 {\r
8638   static char buf[MSG_SIZ];\r
8639   DWORD bufsiz = MSG_SIZ;\r
8640 \r
8641   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8642         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8643   }\r
8644   if (!GetUserName(buf, &bufsiz)) {\r
8645     /*DisplayError("Error getting user name", GetLastError());*/\r
8646     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8647   }\r
8648   return buf;\r
8649 }\r
8650 \r
8651 char *\r
8652 HostName()\r
8653 {\r
8654   static char buf[MSG_SIZ];\r
8655   DWORD bufsiz = MSG_SIZ;\r
8656 \r
8657   if (!GetComputerName(buf, &bufsiz)) {\r
8658     /*DisplayError("Error getting host name", GetLastError());*/\r
8659     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8660   }\r
8661   return buf;\r
8662 }\r
8663 \r
8664 \r
8665 int\r
8666 ClockTimerRunning()\r
8667 {\r
8668   return clockTimerEvent != 0;\r
8669 }\r
8670 \r
8671 int\r
8672 StopClockTimer()\r
8673 {\r
8674   if (clockTimerEvent == 0) return FALSE;\r
8675   KillTimer(hwndMain, clockTimerEvent);\r
8676   clockTimerEvent = 0;\r
8677   return TRUE;\r
8678 }\r
8679 \r
8680 void\r
8681 StartClockTimer(long millisec)\r
8682 {\r
8683   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8684                              (UINT) millisec, NULL);\r
8685 }\r
8686 \r
8687 void\r
8688 DisplayWhiteClock(long timeRemaining, int highlight)\r
8689 {\r
8690   HDC hdc;\r
8691   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8692 \r
8693   if(appData.noGUI) return;\r
8694   hdc = GetDC(hwndMain);\r
8695   if (!IsIconic(hwndMain)) {\r
8696     DisplayAClock(hdc, timeRemaining, highlight, \r
8697                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8698   }\r
8699   if (highlight && iconCurrent == iconBlack) {\r
8700     iconCurrent = iconWhite;\r
8701     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8702     if (IsIconic(hwndMain)) {\r
8703       DrawIcon(hdc, 2, 2, iconCurrent);\r
8704     }\r
8705   }\r
8706   (void) ReleaseDC(hwndMain, hdc);\r
8707   if (hwndConsole)\r
8708     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8709 }\r
8710 \r
8711 void\r
8712 DisplayBlackClock(long timeRemaining, int highlight)\r
8713 {\r
8714   HDC hdc;\r
8715   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8716 \r
8717   if(appData.noGUI) return;\r
8718   hdc = GetDC(hwndMain);\r
8719   if (!IsIconic(hwndMain)) {\r
8720     DisplayAClock(hdc, timeRemaining, highlight, \r
8721                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8722   }\r
8723   if (highlight && iconCurrent == iconWhite) {\r
8724     iconCurrent = iconBlack;\r
8725     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8726     if (IsIconic(hwndMain)) {\r
8727       DrawIcon(hdc, 2, 2, iconCurrent);\r
8728     }\r
8729   }\r
8730   (void) ReleaseDC(hwndMain, hdc);\r
8731   if (hwndConsole)\r
8732     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8733 }\r
8734 \r
8735 \r
8736 int\r
8737 LoadGameTimerRunning()\r
8738 {\r
8739   return loadGameTimerEvent != 0;\r
8740 }\r
8741 \r
8742 int\r
8743 StopLoadGameTimer()\r
8744 {\r
8745   if (loadGameTimerEvent == 0) return FALSE;\r
8746   KillTimer(hwndMain, loadGameTimerEvent);\r
8747   loadGameTimerEvent = 0;\r
8748   return TRUE;\r
8749 }\r
8750 \r
8751 void\r
8752 StartLoadGameTimer(long millisec)\r
8753 {\r
8754   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8755                                 (UINT) millisec, NULL);\r
8756 }\r
8757 \r
8758 void\r
8759 AutoSaveGame()\r
8760 {\r
8761   char *defName;\r
8762   FILE *f;\r
8763   char fileTitle[MSG_SIZ];\r
8764 \r
8765   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8766   f = OpenFileDialog(hwndMain, "a", defName,\r
8767                      appData.oldSaveStyle ? "gam" : "pgn",\r
8768                      GAME_FILT, \r
8769                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8770   if (f != NULL) {\r
8771     SaveGame(f, 0, "");\r
8772     fclose(f);\r
8773   }\r
8774 }\r
8775 \r
8776 \r
8777 void\r
8778 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8779 {\r
8780   if (delayedTimerEvent != 0) {\r
8781     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8782       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8783     }\r
8784     KillTimer(hwndMain, delayedTimerEvent);\r
8785     delayedTimerEvent = 0;\r
8786     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8787     delayedTimerCallback();\r
8788   }\r
8789   delayedTimerCallback = cb;\r
8790   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8791                                 (UINT) millisec, NULL);\r
8792 }\r
8793 \r
8794 DelayedEventCallback\r
8795 GetDelayedEvent()\r
8796 {\r
8797   if (delayedTimerEvent) {\r
8798     return delayedTimerCallback;\r
8799   } else {\r
8800     return NULL;\r
8801   }\r
8802 }\r
8803 \r
8804 void\r
8805 CancelDelayedEvent()\r
8806 {\r
8807   if (delayedTimerEvent) {\r
8808     KillTimer(hwndMain, delayedTimerEvent);\r
8809     delayedTimerEvent = 0;\r
8810   }\r
8811 }\r
8812 \r
8813 DWORD GetWin32Priority(int nice)\r
8814 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8815 /*\r
8816 REALTIME_PRIORITY_CLASS     0x00000100\r
8817 HIGH_PRIORITY_CLASS         0x00000080\r
8818 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8819 NORMAL_PRIORITY_CLASS       0x00000020\r
8820 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8821 IDLE_PRIORITY_CLASS         0x00000040\r
8822 */\r
8823         if (nice < -15) return 0x00000080;\r
8824         if (nice < 0)   return 0x00008000;\r
8825         if (nice == 0)  return 0x00000020;\r
8826         if (nice < 15)  return 0x00004000;\r
8827         return 0x00000040;\r
8828 }\r
8829 \r
8830 /* Start a child process running the given program.\r
8831    The process's standard output can be read from "from", and its\r
8832    standard input can be written to "to".\r
8833    Exit with fatal error if anything goes wrong.\r
8834    Returns an opaque pointer that can be used to destroy the process\r
8835    later.\r
8836 */\r
8837 int\r
8838 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8839 {\r
8840 #define BUFSIZE 4096\r
8841 \r
8842   HANDLE hChildStdinRd, hChildStdinWr,\r
8843     hChildStdoutRd, hChildStdoutWr;\r
8844   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8845   SECURITY_ATTRIBUTES saAttr;\r
8846   BOOL fSuccess;\r
8847   PROCESS_INFORMATION piProcInfo;\r
8848   STARTUPINFO siStartInfo;\r
8849   ChildProc *cp;\r
8850   char buf[MSG_SIZ];\r
8851   DWORD err;\r
8852 \r
8853   if (appData.debugMode) {\r
8854     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8855   }\r
8856 \r
8857   *pr = NoProc;\r
8858 \r
8859   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8860   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8861   saAttr.bInheritHandle = TRUE;\r
8862   saAttr.lpSecurityDescriptor = NULL;\r
8863 \r
8864   /*\r
8865    * The steps for redirecting child's STDOUT:\r
8866    *     1. Create anonymous pipe to be STDOUT for child.\r
8867    *     2. Create a noninheritable duplicate of read handle,\r
8868    *         and close the inheritable read handle.\r
8869    */\r
8870 \r
8871   /* Create a pipe for the child's STDOUT. */\r
8872   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8873     return GetLastError();\r
8874   }\r
8875 \r
8876   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8877   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8878                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8879                              FALSE,     /* not inherited */\r
8880                              DUPLICATE_SAME_ACCESS);\r
8881   if (! fSuccess) {\r
8882     return GetLastError();\r
8883   }\r
8884   CloseHandle(hChildStdoutRd);\r
8885 \r
8886   /*\r
8887    * The steps for redirecting child's STDIN:\r
8888    *     1. Create anonymous pipe to be STDIN for child.\r
8889    *     2. Create a noninheritable duplicate of write handle,\r
8890    *         and close the inheritable write handle.\r
8891    */\r
8892 \r
8893   /* Create a pipe for the child's STDIN. */\r
8894   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8895     return GetLastError();\r
8896   }\r
8897 \r
8898   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8899   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8900                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8901                              FALSE,     /* not inherited */\r
8902                              DUPLICATE_SAME_ACCESS);\r
8903   if (! fSuccess) {\r
8904     return GetLastError();\r
8905   }\r
8906   CloseHandle(hChildStdinWr);\r
8907 \r
8908   /* Arrange to (1) look in dir for the child .exe file, and\r
8909    * (2) have dir be the child's working directory.  Interpret\r
8910    * dir relative to the directory WinBoard loaded from. */\r
8911   GetCurrentDirectory(MSG_SIZ, buf);\r
8912   SetCurrentDirectory(installDir);\r
8913   // kludgey way to update logos in tourney, as long as back-end can't do it\r
8914   if(!strcmp(cmdLine, first.program)) LoadLogo(&first, 0); else\r
8915   if(!strcmp(cmdLine, second.program)) LoadLogo(&second, 1);\r
8916   SetCurrentDirectory(dir);\r
8917 \r
8918   /* Now create the child process. */\r
8919 \r
8920   siStartInfo.cb = sizeof(STARTUPINFO);\r
8921   siStartInfo.lpReserved = NULL;\r
8922   siStartInfo.lpDesktop = NULL;\r
8923   siStartInfo.lpTitle = NULL;\r
8924   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8925   siStartInfo.cbReserved2 = 0;\r
8926   siStartInfo.lpReserved2 = NULL;\r
8927   siStartInfo.hStdInput = hChildStdinRd;\r
8928   siStartInfo.hStdOutput = hChildStdoutWr;\r
8929   siStartInfo.hStdError = hChildStdoutWr;\r
8930 \r
8931   fSuccess = CreateProcess(NULL,\r
8932                            cmdLine,        /* command line */\r
8933                            NULL,           /* process security attributes */\r
8934                            NULL,           /* primary thread security attrs */\r
8935                            TRUE,           /* handles are inherited */\r
8936                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8937                            NULL,           /* use parent's environment */\r
8938                            NULL,\r
8939                            &siStartInfo, /* STARTUPINFO pointer */\r
8940                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8941 \r
8942   err = GetLastError();\r
8943   SetCurrentDirectory(buf); /* return to prev directory */\r
8944   if (! fSuccess) {\r
8945     return err;\r
8946   }\r
8947 \r
8948   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8949     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8950     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8951   }\r
8952 \r
8953   /* Close the handles we don't need in the parent */\r
8954   CloseHandle(piProcInfo.hThread);\r
8955   CloseHandle(hChildStdinRd);\r
8956   CloseHandle(hChildStdoutWr);\r
8957 \r
8958   /* Prepare return value */\r
8959   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8960   cp->kind = CPReal;\r
8961   cp->hProcess = piProcInfo.hProcess;\r
8962   cp->pid = piProcInfo.dwProcessId;\r
8963   cp->hFrom = hChildStdoutRdDup;\r
8964   cp->hTo = hChildStdinWrDup;\r
8965 \r
8966   *pr = (void *) cp;\r
8967 \r
8968   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8969      2000 where engines sometimes don't see the initial command(s)\r
8970      from WinBoard and hang.  I don't understand how that can happen,\r
8971      but the Sleep is harmless, so I've put it in.  Others have also\r
8972      reported what may be the same problem, so hopefully this will fix\r
8973      it for them too.  */\r
8974   Sleep(500);\r
8975 \r
8976   return NO_ERROR;\r
8977 }\r
8978 \r
8979 \r
8980 void\r
8981 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8982 {\r
8983   ChildProc *cp; int result;\r
8984 \r
8985   cp = (ChildProc *) pr;\r
8986   if (cp == NULL) return;\r
8987 \r
8988   switch (cp->kind) {\r
8989   case CPReal:\r
8990     /* TerminateProcess is considered harmful, so... */\r
8991     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8992     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8993     /* The following doesn't work because the chess program\r
8994        doesn't "have the same console" as WinBoard.  Maybe\r
8995        we could arrange for this even though neither WinBoard\r
8996        nor the chess program uses a console for stdio? */\r
8997     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8998 \r
8999     /* [AS] Special termination modes for misbehaving programs... */\r
9000     if( signal == 9 ) { \r
9001         result = TerminateProcess( cp->hProcess, 0 );\r
9002 \r
9003         if ( appData.debugMode) {\r
9004             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9005         }\r
9006     }\r
9007     else if( signal == 10 ) {\r
9008         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9009 \r
9010         if( dw != WAIT_OBJECT_0 ) {\r
9011             result = TerminateProcess( cp->hProcess, 0 );\r
9012 \r
9013             if ( appData.debugMode) {\r
9014                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9015             }\r
9016 \r
9017         }\r
9018     }\r
9019 \r
9020     CloseHandle(cp->hProcess);\r
9021     break;\r
9022 \r
9023   case CPComm:\r
9024     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9025     break;\r
9026 \r
9027   case CPSock:\r
9028     closesocket(cp->sock);\r
9029     WSACleanup();\r
9030     break;\r
9031 \r
9032   case CPRcmd:\r
9033     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9034     closesocket(cp->sock);\r
9035     closesocket(cp->sock2);\r
9036     WSACleanup();\r
9037     break;\r
9038   }\r
9039   free(cp);\r
9040 }\r
9041 \r
9042 void\r
9043 InterruptChildProcess(ProcRef pr)\r
9044 {\r
9045   ChildProc *cp;\r
9046 \r
9047   cp = (ChildProc *) pr;\r
9048   if (cp == NULL) return;\r
9049   switch (cp->kind) {\r
9050   case CPReal:\r
9051     /* The following doesn't work because the chess program\r
9052        doesn't "have the same console" as WinBoard.  Maybe\r
9053        we could arrange for this even though neither WinBoard\r
9054        nor the chess program uses a console for stdio */\r
9055     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9056     break;\r
9057 \r
9058   case CPComm:\r
9059   case CPSock:\r
9060     /* Can't interrupt */\r
9061     break;\r
9062 \r
9063   case CPRcmd:\r
9064     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9065     break;\r
9066   }\r
9067 }\r
9068 \r
9069 \r
9070 int\r
9071 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9072 {\r
9073   char cmdLine[MSG_SIZ];\r
9074 \r
9075   if (port[0] == NULLCHAR) {\r
9076     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9077   } else {\r
9078     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9079   }\r
9080   return StartChildProcess(cmdLine, "", pr);\r
9081 }\r
9082 \r
9083 \r
9084 /* Code to open TCP sockets */\r
9085 \r
9086 int\r
9087 OpenTCP(char *host, char *port, ProcRef *pr)\r
9088 {\r
9089   ChildProc *cp;\r
9090   int err;\r
9091   SOCKET s;\r
9092   struct sockaddr_in sa, mysa;\r
9093   struct hostent FAR *hp;\r
9094   unsigned short uport;\r
9095   WORD wVersionRequested;\r
9096   WSADATA wsaData;\r
9097 \r
9098   /* Initialize socket DLL */\r
9099   wVersionRequested = MAKEWORD(1, 1);\r
9100   err = WSAStartup(wVersionRequested, &wsaData);\r
9101   if (err != 0) return err;\r
9102 \r
9103   /* Make socket */\r
9104   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9105     err = WSAGetLastError();\r
9106     WSACleanup();\r
9107     return err;\r
9108   }\r
9109 \r
9110   /* Bind local address using (mostly) don't-care values.\r
9111    */\r
9112   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9113   mysa.sin_family = AF_INET;\r
9114   mysa.sin_addr.s_addr = INADDR_ANY;\r
9115   uport = (unsigned short) 0;\r
9116   mysa.sin_port = htons(uport);\r
9117   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9118       == SOCKET_ERROR) {\r
9119     err = WSAGetLastError();\r
9120     WSACleanup();\r
9121     return err;\r
9122   }\r
9123 \r
9124   /* Resolve remote host name */\r
9125   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9126   if (!(hp = gethostbyname(host))) {\r
9127     unsigned int b0, b1, b2, b3;\r
9128 \r
9129     err = WSAGetLastError();\r
9130 \r
9131     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9132       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9133       hp->h_addrtype = AF_INET;\r
9134       hp->h_length = 4;\r
9135       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9136       hp->h_addr_list[0] = (char *) malloc(4);\r
9137       hp->h_addr_list[0][0] = (char) b0;\r
9138       hp->h_addr_list[0][1] = (char) b1;\r
9139       hp->h_addr_list[0][2] = (char) b2;\r
9140       hp->h_addr_list[0][3] = (char) b3;\r
9141     } else {\r
9142       WSACleanup();\r
9143       return err;\r
9144     }\r
9145   }\r
9146   sa.sin_family = hp->h_addrtype;\r
9147   uport = (unsigned short) atoi(port);\r
9148   sa.sin_port = htons(uport);\r
9149   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9150 \r
9151   /* Make connection */\r
9152   if (connect(s, (struct sockaddr *) &sa,\r
9153               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9154     err = WSAGetLastError();\r
9155     WSACleanup();\r
9156     return err;\r
9157   }\r
9158 \r
9159   /* Prepare return value */\r
9160   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9161   cp->kind = CPSock;\r
9162   cp->sock = s;\r
9163   *pr = (ProcRef *) cp;\r
9164 \r
9165   return NO_ERROR;\r
9166 }\r
9167 \r
9168 int\r
9169 OpenCommPort(char *name, ProcRef *pr)\r
9170 {\r
9171   HANDLE h;\r
9172   COMMTIMEOUTS ct;\r
9173   ChildProc *cp;\r
9174   char fullname[MSG_SIZ];\r
9175 \r
9176   if (*name != '\\')\r
9177     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9178   else\r
9179     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9180 \r
9181   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9182                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9183   if (h == (HANDLE) -1) {\r
9184     return GetLastError();\r
9185   }\r
9186   hCommPort = h;\r
9187 \r
9188   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9189 \r
9190   /* Accumulate characters until a 100ms pause, then parse */\r
9191   ct.ReadIntervalTimeout = 100;\r
9192   ct.ReadTotalTimeoutMultiplier = 0;\r
9193   ct.ReadTotalTimeoutConstant = 0;\r
9194   ct.WriteTotalTimeoutMultiplier = 0;\r
9195   ct.WriteTotalTimeoutConstant = 0;\r
9196   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9197 \r
9198   /* Prepare return value */\r
9199   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9200   cp->kind = CPComm;\r
9201   cp->hFrom = h;\r
9202   cp->hTo = h;\r
9203   *pr = (ProcRef *) cp;\r
9204 \r
9205   return NO_ERROR;\r
9206 }\r
9207 \r
9208 int\r
9209 OpenLoopback(ProcRef *pr)\r
9210 {\r
9211   DisplayFatalError(_("Not implemented"), 0, 1);\r
9212   return NO_ERROR;\r
9213 }\r
9214 \r
9215 \r
9216 int\r
9217 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9218 {\r
9219   ChildProc *cp;\r
9220   int err;\r
9221   SOCKET s, s2, s3;\r
9222   struct sockaddr_in sa, mysa;\r
9223   struct hostent FAR *hp;\r
9224   unsigned short uport;\r
9225   WORD wVersionRequested;\r
9226   WSADATA wsaData;\r
9227   int fromPort;\r
9228   char stderrPortStr[MSG_SIZ];\r
9229 \r
9230   /* Initialize socket DLL */\r
9231   wVersionRequested = MAKEWORD(1, 1);\r
9232   err = WSAStartup(wVersionRequested, &wsaData);\r
9233   if (err != 0) return err;\r
9234 \r
9235   /* Resolve remote host name */\r
9236   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9237   if (!(hp = gethostbyname(host))) {\r
9238     unsigned int b0, b1, b2, b3;\r
9239 \r
9240     err = WSAGetLastError();\r
9241 \r
9242     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9243       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9244       hp->h_addrtype = AF_INET;\r
9245       hp->h_length = 4;\r
9246       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9247       hp->h_addr_list[0] = (char *) malloc(4);\r
9248       hp->h_addr_list[0][0] = (char) b0;\r
9249       hp->h_addr_list[0][1] = (char) b1;\r
9250       hp->h_addr_list[0][2] = (char) b2;\r
9251       hp->h_addr_list[0][3] = (char) b3;\r
9252     } else {\r
9253       WSACleanup();\r
9254       return err;\r
9255     }\r
9256   }\r
9257   sa.sin_family = hp->h_addrtype;\r
9258   uport = (unsigned short) 514;\r
9259   sa.sin_port = htons(uport);\r
9260   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9261 \r
9262   /* Bind local socket to unused "privileged" port address\r
9263    */\r
9264   s = INVALID_SOCKET;\r
9265   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9266   mysa.sin_family = AF_INET;\r
9267   mysa.sin_addr.s_addr = INADDR_ANY;\r
9268   for (fromPort = 1023;; fromPort--) {\r
9269     if (fromPort < 0) {\r
9270       WSACleanup();\r
9271       return WSAEADDRINUSE;\r
9272     }\r
9273     if (s == INVALID_SOCKET) {\r
9274       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9275         err = WSAGetLastError();\r
9276         WSACleanup();\r
9277         return err;\r
9278       }\r
9279     }\r
9280     uport = (unsigned short) fromPort;\r
9281     mysa.sin_port = htons(uport);\r
9282     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9283         == SOCKET_ERROR) {\r
9284       err = WSAGetLastError();\r
9285       if (err == WSAEADDRINUSE) continue;\r
9286       WSACleanup();\r
9287       return err;\r
9288     }\r
9289     if (connect(s, (struct sockaddr *) &sa,\r
9290       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9291       err = WSAGetLastError();\r
9292       if (err == WSAEADDRINUSE) {\r
9293         closesocket(s);\r
9294         s = -1;\r
9295         continue;\r
9296       }\r
9297       WSACleanup();\r
9298       return err;\r
9299     }\r
9300     break;\r
9301   }\r
9302 \r
9303   /* Bind stderr local socket to unused "privileged" port address\r
9304    */\r
9305   s2 = INVALID_SOCKET;\r
9306   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9307   mysa.sin_family = AF_INET;\r
9308   mysa.sin_addr.s_addr = INADDR_ANY;\r
9309   for (fromPort = 1023;; fromPort--) {\r
9310     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9311     if (fromPort < 0) {\r
9312       (void) closesocket(s);\r
9313       WSACleanup();\r
9314       return WSAEADDRINUSE;\r
9315     }\r
9316     if (s2 == INVALID_SOCKET) {\r
9317       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9318         err = WSAGetLastError();\r
9319         closesocket(s);\r
9320         WSACleanup();\r
9321         return err;\r
9322       }\r
9323     }\r
9324     uport = (unsigned short) fromPort;\r
9325     mysa.sin_port = htons(uport);\r
9326     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9327         == SOCKET_ERROR) {\r
9328       err = WSAGetLastError();\r
9329       if (err == WSAEADDRINUSE) continue;\r
9330       (void) closesocket(s);\r
9331       WSACleanup();\r
9332       return err;\r
9333     }\r
9334     if (listen(s2, 1) == SOCKET_ERROR) {\r
9335       err = WSAGetLastError();\r
9336       if (err == WSAEADDRINUSE) {\r
9337         closesocket(s2);\r
9338         s2 = INVALID_SOCKET;\r
9339         continue;\r
9340       }\r
9341       (void) closesocket(s);\r
9342       (void) closesocket(s2);\r
9343       WSACleanup();\r
9344       return err;\r
9345     }\r
9346     break;\r
9347   }\r
9348   prevStderrPort = fromPort; // remember port used\r
9349   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9350 \r
9351   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9352     err = WSAGetLastError();\r
9353     (void) closesocket(s);\r
9354     (void) closesocket(s2);\r
9355     WSACleanup();\r
9356     return err;\r
9357   }\r
9358 \r
9359   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9360     err = WSAGetLastError();\r
9361     (void) closesocket(s);\r
9362     (void) closesocket(s2);\r
9363     WSACleanup();\r
9364     return err;\r
9365   }\r
9366   if (*user == NULLCHAR) user = UserName();\r
9367   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9368     err = WSAGetLastError();\r
9369     (void) closesocket(s);\r
9370     (void) closesocket(s2);\r
9371     WSACleanup();\r
9372     return err;\r
9373   }\r
9374   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9375     err = WSAGetLastError();\r
9376     (void) closesocket(s);\r
9377     (void) closesocket(s2);\r
9378     WSACleanup();\r
9379     return err;\r
9380   }\r
9381 \r
9382   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9383     err = WSAGetLastError();\r
9384     (void) closesocket(s);\r
9385     (void) closesocket(s2);\r
9386     WSACleanup();\r
9387     return err;\r
9388   }\r
9389   (void) closesocket(s2);  /* Stop listening */\r
9390 \r
9391   /* Prepare return value */\r
9392   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9393   cp->kind = CPRcmd;\r
9394   cp->sock = s;\r
9395   cp->sock2 = s3;\r
9396   *pr = (ProcRef *) cp;\r
9397 \r
9398   return NO_ERROR;\r
9399 }\r
9400 \r
9401 \r
9402 InputSourceRef\r
9403 AddInputSource(ProcRef pr, int lineByLine,\r
9404                InputCallback func, VOIDSTAR closure)\r
9405 {\r
9406   InputSource *is, *is2 = NULL;\r
9407   ChildProc *cp = (ChildProc *) pr;\r
9408 \r
9409   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9410   is->lineByLine = lineByLine;\r
9411   is->func = func;\r
9412   is->closure = closure;\r
9413   is->second = NULL;\r
9414   is->next = is->buf;\r
9415   if (pr == NoProc) {\r
9416     is->kind = CPReal;\r
9417     consoleInputSource = is;\r
9418   } else {\r
9419     is->kind = cp->kind;\r
9420     /* \r
9421         [AS] Try to avoid a race condition if the thread is given control too early:\r
9422         we create all threads suspended so that the is->hThread variable can be\r
9423         safely assigned, then let the threads start with ResumeThread.\r
9424     */\r
9425     switch (cp->kind) {\r
9426     case CPReal:\r
9427       is->hFile = cp->hFrom;\r
9428       cp->hFrom = NULL; /* now owned by InputThread */\r
9429       is->hThread =\r
9430         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9431                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9432       break;\r
9433 \r
9434     case CPComm:\r
9435       is->hFile = cp->hFrom;\r
9436       cp->hFrom = NULL; /* now owned by InputThread */\r
9437       is->hThread =\r
9438         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9439                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9440       break;\r
9441 \r
9442     case CPSock:\r
9443       is->sock = cp->sock;\r
9444       is->hThread =\r
9445         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9446                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9447       break;\r
9448 \r
9449     case CPRcmd:\r
9450       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9451       *is2 = *is;\r
9452       is->sock = cp->sock;\r
9453       is->second = is2;\r
9454       is2->sock = cp->sock2;\r
9455       is2->second = is2;\r
9456       is->hThread =\r
9457         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9458                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9459       is2->hThread =\r
9460         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9461                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9462       break;\r
9463     }\r
9464 \r
9465     if( is->hThread != NULL ) {\r
9466         ResumeThread( is->hThread );\r
9467     }\r
9468 \r
9469     if( is2 != NULL && is2->hThread != NULL ) {\r
9470         ResumeThread( is2->hThread );\r
9471     }\r
9472   }\r
9473 \r
9474   return (InputSourceRef) is;\r
9475 }\r
9476 \r
9477 void\r
9478 RemoveInputSource(InputSourceRef isr)\r
9479 {\r
9480   InputSource *is;\r
9481 \r
9482   is = (InputSource *) isr;\r
9483   is->hThread = NULL;  /* tell thread to stop */\r
9484   CloseHandle(is->hThread);\r
9485   if (is->second != NULL) {\r
9486     is->second->hThread = NULL;\r
9487     CloseHandle(is->second->hThread);\r
9488   }\r
9489 }\r
9490 \r
9491 int no_wrap(char *message, int count)\r
9492 {\r
9493     ConsoleOutput(message, count, FALSE);\r
9494     return count;\r
9495 }\r
9496 \r
9497 int\r
9498 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9499 {\r
9500   DWORD dOutCount;\r
9501   int outCount = SOCKET_ERROR;\r
9502   ChildProc *cp = (ChildProc *) pr;\r
9503   static OVERLAPPED ovl;\r
9504   static int line = 0;\r
9505 \r
9506   if (pr == NoProc)\r
9507   {\r
9508     if (appData.noJoin || !appData.useInternalWrap)\r
9509       return no_wrap(message, count);\r
9510     else\r
9511     {\r
9512       int width = get_term_width();\r
9513       int len = wrap(NULL, message, count, width, &line);\r
9514       char *msg = malloc(len);\r
9515       int dbgchk;\r
9516 \r
9517       if (!msg)\r
9518         return no_wrap(message, count);\r
9519       else\r
9520       {\r
9521         dbgchk = wrap(msg, message, count, width, &line);\r
9522         if (dbgchk != len && appData.debugMode)\r
9523             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9524         ConsoleOutput(msg, len, FALSE);\r
9525         free(msg);\r
9526         return len;\r
9527       }\r
9528     }\r
9529   }\r
9530 \r
9531   if (ovl.hEvent == NULL) {\r
9532     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9533   }\r
9534   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9535 \r
9536   switch (cp->kind) {\r
9537   case CPSock:\r
9538   case CPRcmd:\r
9539     outCount = send(cp->sock, message, count, 0);\r
9540     if (outCount == SOCKET_ERROR) {\r
9541       *outError = WSAGetLastError();\r
9542     } else {\r
9543       *outError = NO_ERROR;\r
9544     }\r
9545     break;\r
9546 \r
9547   case CPReal:\r
9548     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9549                   &dOutCount, NULL)) {\r
9550       *outError = NO_ERROR;\r
9551       outCount = (int) dOutCount;\r
9552     } else {\r
9553       *outError = GetLastError();\r
9554     }\r
9555     break;\r
9556 \r
9557   case CPComm:\r
9558     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9559                             &dOutCount, &ovl);\r
9560     if (*outError == NO_ERROR) {\r
9561       outCount = (int) dOutCount;\r
9562     }\r
9563     break;\r
9564   }\r
9565   return outCount;\r
9566 }\r
9567 \r
9568 int\r
9569 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9570                        long msdelay)\r
9571 {\r
9572   /* Ignore delay, not implemented for WinBoard */\r
9573   return OutputToProcess(pr, message, count, outError);\r
9574 }\r
9575 \r
9576 \r
9577 void\r
9578 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9579                         char *buf, int count, int error)\r
9580 {\r
9581   DisplayFatalError(_("Not implemented"), 0, 1);\r
9582 }\r
9583 \r
9584 /* see wgamelist.c for Game List functions */\r
9585 /* see wedittags.c for Edit Tags functions */\r
9586 \r
9587 \r
9588 VOID\r
9589 ICSInitScript()\r
9590 {\r
9591   FILE *f;\r
9592   char buf[MSG_SIZ];\r
9593   char *dummy;\r
9594 \r
9595   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9596     f = fopen(buf, "r");\r
9597     if (f != NULL) {\r
9598       ProcessICSInitScript(f);\r
9599       fclose(f);\r
9600     }\r
9601   }\r
9602 }\r
9603 \r
9604 \r
9605 VOID\r
9606 StartAnalysisClock()\r
9607 {\r
9608   if (analysisTimerEvent) return;\r
9609   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9610                                         (UINT) 2000, NULL);\r
9611 }\r
9612 \r
9613 VOID\r
9614 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9615 {\r
9616   highlightInfo.sq[0].x = fromX;\r
9617   highlightInfo.sq[0].y = fromY;\r
9618   highlightInfo.sq[1].x = toX;\r
9619   highlightInfo.sq[1].y = toY;\r
9620 }\r
9621 \r
9622 VOID\r
9623 ClearHighlights()\r
9624 {\r
9625   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9626     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9627 }\r
9628 \r
9629 VOID\r
9630 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9631 {\r
9632   premoveHighlightInfo.sq[0].x = fromX;\r
9633   premoveHighlightInfo.sq[0].y = fromY;\r
9634   premoveHighlightInfo.sq[1].x = toX;\r
9635   premoveHighlightInfo.sq[1].y = toY;\r
9636 }\r
9637 \r
9638 VOID\r
9639 ClearPremoveHighlights()\r
9640 {\r
9641   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9642     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9643 }\r
9644 \r
9645 VOID\r
9646 ShutDownFrontEnd()\r
9647 {\r
9648   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9649   DeleteClipboardTempFiles();\r
9650 }\r
9651 \r
9652 void\r
9653 BoardToTop()\r
9654 {\r
9655     if (IsIconic(hwndMain))\r
9656       ShowWindow(hwndMain, SW_RESTORE);\r
9657 \r
9658     SetActiveWindow(hwndMain);\r
9659 }\r
9660 \r
9661 /*\r
9662  * Prototypes for animation support routines\r
9663  */\r
9664 static void ScreenSquare(int column, int row, POINT * pt);\r
9665 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9666      POINT frames[], int * nFrames);\r
9667 \r
9668 \r
9669 #define kFactor 4\r
9670 \r
9671 void\r
9672 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9673 {       // [HGM] atomic: animate blast wave\r
9674         int i;\r
9675 \r
9676         explodeInfo.fromX = fromX;\r
9677         explodeInfo.fromY = fromY;\r
9678         explodeInfo.toX = toX;\r
9679         explodeInfo.toY = toY;\r
9680         for(i=1; i<4*kFactor; i++) {\r
9681             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9682             DrawPosition(FALSE, board);\r
9683             Sleep(appData.animSpeed);\r
9684         }\r
9685         explodeInfo.radius = 0;\r
9686         DrawPosition(TRUE, board);\r
9687 }\r
9688 \r
9689 void\r
9690 AnimateMove(board, fromX, fromY, toX, toY)\r
9691      Board board;\r
9692      int fromX;\r
9693      int fromY;\r
9694      int toX;\r
9695      int toY;\r
9696 {\r
9697   ChessSquare piece;\r
9698   POINT start, finish, mid;\r
9699   POINT frames[kFactor * 2 + 1];\r
9700   int nFrames, n;\r
9701 \r
9702   if (!appData.animate) return;\r
9703   if (doingSizing) return;\r
9704   if (fromY < 0 || fromX < 0) return;\r
9705   piece = board[fromY][fromX];\r
9706   if (piece >= EmptySquare) return;\r
9707 \r
9708   ScreenSquare(fromX, fromY, &start);\r
9709   ScreenSquare(toX, toY, &finish);\r
9710 \r
9711   /* All moves except knight jumps move in straight line */\r
9712   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9713     mid.x = start.x + (finish.x - start.x) / 2;\r
9714     mid.y = start.y + (finish.y - start.y) / 2;\r
9715   } else {\r
9716     /* Knight: make straight movement then diagonal */\r
9717     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9718        mid.x = start.x + (finish.x - start.x) / 2;\r
9719        mid.y = start.y;\r
9720      } else {\r
9721        mid.x = start.x;\r
9722        mid.y = start.y + (finish.y - start.y) / 2;\r
9723      }\r
9724   }\r
9725   \r
9726   /* Don't use as many frames for very short moves */\r
9727   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9728     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9729   else\r
9730     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9731 \r
9732   animInfo.from.x = fromX;\r
9733   animInfo.from.y = fromY;\r
9734   animInfo.to.x = toX;\r
9735   animInfo.to.y = toY;\r
9736   animInfo.lastpos = start;\r
9737   animInfo.piece = piece;\r
9738   for (n = 0; n < nFrames; n++) {\r
9739     animInfo.pos = frames[n];\r
9740     DrawPosition(FALSE, NULL);\r
9741     animInfo.lastpos = animInfo.pos;\r
9742     Sleep(appData.animSpeed);\r
9743   }\r
9744   animInfo.pos = finish;\r
9745   DrawPosition(FALSE, NULL);\r
9746   animInfo.piece = EmptySquare;\r
9747   Explode(board, fromX, fromY, toX, toY);\r
9748 }\r
9749 \r
9750 /*      Convert board position to corner of screen rect and color       */\r
9751 \r
9752 static void\r
9753 ScreenSquare(column, row, pt)\r
9754      int column; int row; POINT * pt;\r
9755 {\r
9756   if (flipView) {\r
9757     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9758     pt->y = lineGap + row * (squareSize + lineGap);\r
9759   } else {\r
9760     pt->x = lineGap + column * (squareSize + lineGap);\r
9761     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9762   }\r
9763 }\r
9764 \r
9765 /*      Generate a series of frame coords from start->mid->finish.\r
9766         The movement rate doubles until the half way point is\r
9767         reached, then halves back down to the final destination,\r
9768         which gives a nice slow in/out effect. The algorithmn\r
9769         may seem to generate too many intermediates for short\r
9770         moves, but remember that the purpose is to attract the\r
9771         viewers attention to the piece about to be moved and\r
9772         then to where it ends up. Too few frames would be less\r
9773         noticeable.                                             */\r
9774 \r
9775 static void\r
9776 Tween(start, mid, finish, factor, frames, nFrames)\r
9777      POINT * start; POINT * mid;\r
9778      POINT * finish; int factor;\r
9779      POINT frames[]; int * nFrames;\r
9780 {\r
9781   int n, fraction = 1, count = 0;\r
9782 \r
9783   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9784   for (n = 0; n < factor; n++)\r
9785     fraction *= 2;\r
9786   for (n = 0; n < factor; n++) {\r
9787     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9788     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9789     count ++;\r
9790     fraction = fraction / 2;\r
9791   }\r
9792   \r
9793   /* Midpoint */\r
9794   frames[count] = *mid;\r
9795   count ++;\r
9796   \r
9797   /* Slow out, stepping 1/2, then 1/4, ... */\r
9798   fraction = 2;\r
9799   for (n = 0; n < factor; n++) {\r
9800     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9801     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9802     count ++;\r
9803     fraction = fraction * 2;\r
9804   }\r
9805   *nFrames = count;\r
9806 }\r
9807 \r
9808 void\r
9809 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9810 {\r
9811     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9812 \r
9813     EvalGraphSet( first, last, current, pvInfoList );\r
9814 }\r
9815 \r
9816 void\r
9817 SettingsPopUp(ChessProgramState *cps)\r
9818 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9819       EngineOptionsPopup(savedHwnd, cps);\r
9820 }\r
9821 \r
9822 int flock(int fid, int code)\r
9823 {\r
9824     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9825     OVERLAPPED ov;\r
9826     ov.hEvent = NULL;\r
9827     ov.Offset = 0;\r
9828     ov.OffsetHigh = 0;\r
9829     switch(code) {\r
9830       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9831       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9832       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9833       default: return -1;\r
9834     }\r
9835     return 0;\r
9836 }\r