Fix logo repainting
[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 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 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void DisplayMove P((int moveNumber));\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
110 void ChatPopUp P((char *s));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
138 \r
139 typedef struct { // [HGM] atomic\r
140   int fromX, fromY, toX, toY, radius;\r
141 } ExplodeInfo;\r
142 \r
143 static ExplodeInfo explodeInfo;\r
144 \r
145 /* Window class names */\r
146 char szAppName[] = "WinBoard";\r
147 char szConsoleName[] = "WBConsole";\r
148 \r
149 /* Title bar text */\r
150 char szTitle[] = "WinBoard";\r
151 char szConsoleTitle[] = "I C S Interaction";\r
152 \r
153 char *programName;\r
154 char *settingsFileName;\r
155 Boolean saveSettingsOnExit;\r
156 char installDir[MSG_SIZ];\r
157 int errorExitStatus;\r
158 \r
159 BoardSize boardSize;\r
160 Boolean chessProgram;\r
161 //static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 int squareSize, lineGap, minorSize;\r
164 static int winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define PALETTESIZE 256\r
180 \r
181 HINSTANCE hInst;          /* current instance */\r
182 Boolean alwaysOnTop = FALSE;\r
183 RECT boardRect;\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
185   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
186 HPALETTE hPal;\r
187 ColorClass currentColorClass;\r
188 \r
189 static HWND savedHwnd;\r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   markerBrush,      /* [HGM] markers */\r
197   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
200 static HPEN gridPen = NULL;\r
201 static HPEN highlightPen = NULL;\r
202 static HPEN premovePen = NULL;\r
203 static NPLOGPALETTE pLogPal;\r
204 static BOOL paletteChanged = FALSE;\r
205 static HICON iconWhite, iconBlack, iconCurrent;\r
206 static int doingSizing = FALSE;\r
207 static int lastSizing = 0;\r
208 static int prevStderrPort;\r
209 static HBITMAP userLogo;\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #if defined(_winmajor)\r
222 #define oldDialog (_winmajor < 4)\r
223 #else\r
224 #define oldDialog 0\r
225 #endif\r
226 #endif\r
227 \r
228 #define INTERNATIONAL\r
229 \r
230 #ifdef INTERNATIONAL\r
231 #  define _(s) T_(s)\r
232 #  define N_(s) s\r
233 #else\r
234 #  define _(s) s\r
235 #  define N_(s) s\r
236 #  define T_(s) s\r
237 #  define Translate(x, y)\r
238 #  define LoadLanguageFile(s)\r
239 #endif\r
240 \r
241 #ifdef INTERNATIONAL\r
242 \r
243 Boolean barbaric; // flag indicating if translation is needed\r
244 \r
245 // list of item numbers used in each dialog (used to alter language at run time)\r
246 \r
247 #define ABOUTBOX -1  /* not sure why these are needed */\r
248 #define ABOUTBOX2 -1\r
249 \r
250 int dialogItems[][40] = {\r
251 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][10];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
349     if((f = fopen(buf, "r")) == NULL) return;\r
350     while((k = fgetc(f)) != EOF) {\r
351         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
352         languageBuf[i] = k;\r
353         if(k == '\n') {\r
354             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
355                 char *p;\r
356                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
357                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
358                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
359                         english[j] = languageBuf + n + 1; *p = 0;\r
360                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
362                     }\r
363                 }\r
364             }\r
365             n = i + 1;\r
366         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
367             switch(k) {\r
368               case 'n': k = '\n'; break;\r
369               case 'r': k = '\r'; break;\r
370               case 't': k = '\t'; break;\r
371             }\r
372             languageBuf[--i] = k;\r
373         }\r
374         i++;\r
375     }\r
376     fclose(f);\r
377     barbaric = (j != 0);\r
378     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
379 }\r
380 \r
381 char *\r
382 T_(char *s)\r
383 {   // return the translation of the given string\r
384     // efficiency can be improved a lot...\r
385     int i=0;\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
387     if(!barbaric) return s;\r
388     if(!s) return ""; // sanity\r
389     while(english[i]) {\r
390         if(!strcmp(s, english[i])) return foreign[i];\r
391         i++;\r
392     }\r
393     return s;\r
394 }\r
395 \r
396 void\r
397 Translate(HWND hDlg, int dialogID)\r
398 {   // translate all text items in the given dialog\r
399     int i=0, j, k;\r
400     char buf[MSG_SIZ], *s;\r
401     if(!barbaric) return;\r
402     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
403     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
404     GetWindowText( hDlg, buf, MSG_SIZ );\r
405     s = T_(buf);\r
406     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
407     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
408         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
409         if(strlen(buf) == 0) continue;\r
410         s = T_(buf);\r
411         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
412     }\r
413 }\r
414 \r
415 HMENU\r
416 TranslateOneMenu(int i, HMENU subMenu)\r
417 {\r
418     int j;\r
419     static MENUITEMINFO info;\r
420 \r
421     info.cbSize = sizeof(MENUITEMINFO);\r
422     info.fMask = MIIM_STATE | MIIM_TYPE;\r
423           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
424             char buf[MSG_SIZ];\r
425             info.dwTypeData = buf;\r
426             info.cch = sizeof(buf);\r
427             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
428             if(i < 10) {
429                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
430                 else menuText[i][j] = strdup(buf); // remember original on first change\r
431             }\r
432             if(buf[0] == NULLCHAR) continue;\r
433             info.dwTypeData = T_(buf);\r
434             info.cch = strlen(buf)+1;\r
435             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
436           }\r
437     return subMenu;\r
438 }\r
439 \r
440 void\r
441 TranslateMenus(int addLanguage)\r
442 {\r
443     int i;\r
444     WIN32_FIND_DATA fileData;\r
445     HANDLE hFind;\r
446 #define IDM_English 1895\r
447     if(1) {\r
448         HMENU mainMenu = GetMenu(hwndMain);\r
449         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
450           HMENU subMenu = GetSubMenu(mainMenu, i);\r
451           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
452                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
453           TranslateOneMenu(i, subMenu);\r
454         }\r
455         DrawMenuBar(hwndMain);\r
456     }\r
457 \r
458     if(!addLanguage) return;\r
459     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
462         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
463         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
464         i = 0; lastChecked = IDM_English;\r
465         do {\r
466             char *p, *q = fileData.cFileName;\r
467             int checkFlag = MF_UNCHECKED;\r
468             languageFile[i] = strdup(q);\r
469             if(barbaric && !strcmp(oldLanguage, q)) {\r
470                 checkFlag = MF_CHECKED;\r
471                 lastChecked = IDM_English + i + 1;\r
472                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
473             }\r
474             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
475             p = strstr(fileData.cFileName, ".lng");\r
476             if(p) *p = 0;\r
477             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
478         } while(FindNextFile(hFind, &fileData));\r
479         FindClose(hFind);\r
480     }\r
481 }\r
482 \r
483 #endif\r
484 \r
485 typedef struct {\r
486   char *name;\r
487   int squareSize;\r
488   int lineGap;\r
489   int smallLayout;\r
490   int tinyLayout;\r
491   int cliWidth, cliHeight;\r
492 } SizeInfo;\r
493 \r
494 SizeInfo sizeInfo[] = \r
495 {\r
496   { "tiny",     21, 0, 1, 1, 0, 0 },\r
497   { "teeny",    25, 1, 1, 1, 0, 0 },\r
498   { "dinky",    29, 1, 1, 1, 0, 0 },\r
499   { "petite",   33, 1, 1, 1, 0, 0 },\r
500   { "slim",     37, 2, 1, 0, 0, 0 },\r
501   { "small",    40, 2, 1, 0, 0, 0 },\r
502   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
503   { "middling", 49, 2, 0, 0, 0, 0 },\r
504   { "average",  54, 2, 0, 0, 0, 0 },\r
505   { "moderate", 58, 3, 0, 0, 0, 0 },\r
506   { "medium",   64, 3, 0, 0, 0, 0 },\r
507   { "bulky",    72, 3, 0, 0, 0, 0 },\r
508   { "large",    80, 3, 0, 0, 0, 0 },\r
509   { "big",      87, 3, 0, 0, 0, 0 },\r
510   { "huge",     95, 3, 0, 0, 0, 0 },\r
511   { "giant",    108, 3, 0, 0, 0, 0 },\r
512   { "colossal", 116, 4, 0, 0, 0, 0 },\r
513   { "titanic",  129, 4, 0, 0, 0, 0 },\r
514   { NULL, 0, 0, 0, 0, 0, 0 }\r
515 };\r
516 \r
517 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
518 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
519 {\r
520   { 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
521   { 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
522   { 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
523   { 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
524   { 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
525   { 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
526   { 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
527   { 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
528   { 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
529   { 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
530   { 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
531   { 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
532   { 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
533   { 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
534   { 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
535   { 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
536   { 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
537   { 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
538 };\r
539 \r
540 MyFont *font[NUM_SIZES][NUM_FONTS];\r
541 \r
542 typedef struct {\r
543   char *label;\r
544   int id;\r
545   HWND hwnd;\r
546   WNDPROC wndproc;\r
547 } MyButtonDesc;\r
548 \r
549 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
550 #define N_BUTTONS 5\r
551 \r
552 MyButtonDesc buttonDesc[N_BUTTONS] =\r
553 {\r
554   {"<<", IDM_ToStart, NULL, NULL},\r
555   {"<", IDM_Backward, NULL, NULL},\r
556   {"P", IDM_Pause, NULL, NULL},\r
557   {">", IDM_Forward, NULL, NULL},\r
558   {">>", IDM_ToEnd, NULL, NULL},\r
559 };\r
560 \r
561 int tinyLayout = 0, smallLayout = 0;\r
562 #define MENU_BAR_ITEMS 9\r
563 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
564   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
565   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
566 };\r
567 \r
568 \r
569 MySound sounds[(int)NSoundClasses];\r
570 MyTextAttribs textAttribs[(int)NColorClasses];\r
571 \r
572 MyColorizeAttribs colorizeAttribs[] = {\r
573   { (COLORREF)0, 0, N_("Shout Text") },\r
574   { (COLORREF)0, 0, N_("SShout/CShout") },\r
575   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
576   { (COLORREF)0, 0, N_("Channel Text") },\r
577   { (COLORREF)0, 0, N_("Kibitz Text") },\r
578   { (COLORREF)0, 0, N_("Tell Text") },\r
579   { (COLORREF)0, 0, N_("Challenge Text") },\r
580   { (COLORREF)0, 0, N_("Request Text") },\r
581   { (COLORREF)0, 0, N_("Seek Text") },\r
582   { (COLORREF)0, 0, N_("Normal Text") },\r
583   { (COLORREF)0, 0, N_("None") }\r
584 };\r
585 \r
586 \r
587 \r
588 static char *commentTitle;\r
589 static char *commentText;\r
590 static int commentIndex;\r
591 static Boolean editComment = FALSE;\r
592 \r
593 \r
594 char errorTitle[MSG_SIZ];\r
595 char errorMessage[2*MSG_SIZ];\r
596 HWND errorDialog = NULL;\r
597 BOOLEAN moveErrorMessageUp = FALSE;\r
598 BOOLEAN consoleEcho = TRUE;\r
599 CHARFORMAT consoleCF;\r
600 COLORREF consoleBackgroundColor;\r
601 \r
602 char *programVersion;\r
603 \r
604 #define CPReal 1\r
605 #define CPComm 2\r
606 #define CPSock 3\r
607 #define CPRcmd 4\r
608 typedef int CPKind;\r
609 \r
610 typedef struct {\r
611   CPKind kind;\r
612   HANDLE hProcess;\r
613   DWORD pid;\r
614   HANDLE hTo;\r
615   HANDLE hFrom;\r
616   SOCKET sock;\r
617   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
618 } ChildProc;\r
619 \r
620 #define INPUT_SOURCE_BUF_SIZE 4096\r
621 \r
622 typedef struct _InputSource {\r
623   CPKind kind;\r
624   HANDLE hFile;\r
625   SOCKET sock;\r
626   int lineByLine;\r
627   HANDLE hThread;\r
628   DWORD id;\r
629   char buf[INPUT_SOURCE_BUF_SIZE];\r
630   char *next;\r
631   DWORD count;\r
632   int error;\r
633   InputCallback func;\r
634   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
635   VOIDSTAR closure;\r
636 } InputSource;\r
637 \r
638 InputSource *consoleInputSource;\r
639 \r
640 DCB dcb;\r
641 \r
642 /* forward */\r
643 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
644 VOID ConsoleCreate();\r
645 LRESULT CALLBACK\r
646   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
647 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
648 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
649 VOID ParseCommSettings(char *arg, DCB *dcb);\r
650 LRESULT CALLBACK\r
651   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
652 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
653 void ParseIcsTextMenu(char *icsTextMenuString);\r
654 VOID PopUpMoveDialog(char firstchar);\r
655 VOID PopUpNameDialog(char firstchar);\r
656 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
657 \r
658 /* [AS] */\r
659 int NewGameFRC();\r
660 int GameListOptions();\r
661 \r
662 int dummy; // [HGM] for obsolete args\r
663 \r
664 HWND hwndMain = NULL;        /* root window*/\r
665 HWND hwndConsole = NULL;\r
666 HWND commentDialog = NULL;\r
667 HWND moveHistoryDialog = NULL;\r
668 HWND evalGraphDialog = NULL;\r
669 HWND engineOutputDialog = NULL;\r
670 HWND gameListDialog = NULL;\r
671 HWND editTagsDialog = NULL;\r
672 \r
673 int commentUp = FALSE;\r
674 \r
675 WindowPlacement wpMain;\r
676 WindowPlacement wpConsole;\r
677 WindowPlacement wpComment;\r
678 WindowPlacement wpMoveHistory;\r
679 WindowPlacement wpEvalGraph;\r
680 WindowPlacement wpEngineOutput;\r
681 WindowPlacement wpGameList;\r
682 WindowPlacement wpTags;\r
683 \r
684 VOID EngineOptionsPopup(); // [HGM] settings\r
685 \r
686 VOID GothicPopUp(char *title, VariantClass variant);\r
687 /*\r
688  * Setting "frozen" should disable all user input other than deleting\r
689  * the window.  We do this while engines are initializing themselves.\r
690  */\r
691 static int frozen = 0;\r
692 static int oldMenuItemState[MENU_BAR_ITEMS];\r
693 void FreezeUI()\r
694 {\r
695   HMENU hmenu;\r
696   int i;\r
697 \r
698   if (frozen) return;\r
699   frozen = 1;\r
700   hmenu = GetMenu(hwndMain);\r
701   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
702     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
703   }\r
704   DrawMenuBar(hwndMain);\r
705 }\r
706 \r
707 /* Undo a FreezeUI */\r
708 void ThawUI()\r
709 {\r
710   HMENU hmenu;\r
711   int i;\r
712 \r
713   if (!frozen) return;\r
714   frozen = 0;\r
715   hmenu = GetMenu(hwndMain);\r
716   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
717     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
718   }\r
719   DrawMenuBar(hwndMain);\r
720 }\r
721 \r
722 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
723 \r
724 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
725 #ifdef JAWS\r
726 #include "jaws.c"\r
727 #else\r
728 #define JAWS_INIT\r
729 #define JAWS_ARGS\r
730 #define JAWS_ALT_INTERCEPT\r
731 #define JAWS_KB_NAVIGATION\r
732 #define JAWS_MENU_ITEMS\r
733 #define JAWS_SILENCE\r
734 #define JAWS_REPLAY\r
735 #define JAWS_ACCEL\r
736 #define JAWS_COPYRIGHT\r
737 #define JAWS_DELETE(X) X\r
738 #define SAYMACHINEMOVE()\r
739 #define SAY(X)\r
740 #endif\r
741 \r
742 /*---------------------------------------------------------------------------*\\r
743  *\r
744  * WinMain\r
745  *\r
746 \*---------------------------------------------------------------------------*/\r
747 \r
748 int APIENTRY\r
749 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
750         LPSTR lpCmdLine, int nCmdShow)\r
751 {\r
752   MSG msg;\r
753   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
754 //  INITCOMMONCONTROLSEX ex;\r
755 \r
756   debugFP = stderr;\r
757 \r
758   LoadLibrary("RICHED32.DLL");\r
759   consoleCF.cbSize = sizeof(CHARFORMAT);\r
760 \r
761   if (!InitApplication(hInstance)) {\r
762     return (FALSE);\r
763   }\r
764   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
765     return (FALSE);\r
766   }\r
767 \r
768   JAWS_INIT\r
769   TranslateMenus(1);\r
770 \r
771 //  InitCommonControlsEx(&ex);\r
772   InitCommonControls();\r
773 \r
774   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
775   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
776   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
777 \r
778   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
779 \r
780   while (GetMessage(&msg, /* message structure */\r
781                     NULL, /* handle of window receiving the message */\r
782                     0,    /* lowest message to examine */\r
783                     0))   /* highest message to examine */\r
784     {\r
785 \r
786       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
787         // [HGM] navigate: switch between all windows with tab\r
788         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
789         int i, currentElement = 0;\r
790 \r
791         // first determine what element of the chain we come from (if any)\r
792         if(appData.icsActive) {\r
793             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
794             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
795         }\r
796         if(engineOutputDialog && EngineOutputIsUp()) {\r
797             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
798             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
799         }\r
800         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
801             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
802         }\r
803         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
804         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
805         if(msg.hwnd == e1)                 currentElement = 2; else\r
806         if(msg.hwnd == e2)                 currentElement = 3; else\r
807         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
808         if(msg.hwnd == mh)                currentElement = 4; else\r
809         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
810         if(msg.hwnd == hText)  currentElement = 5; else\r
811         if(msg.hwnd == hInput) currentElement = 6; else\r
812         for (i = 0; i < N_BUTTONS; i++) {\r
813             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
814         }\r
815 \r
816         // determine where to go to\r
817         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
818           do {\r
819             currentElement = (currentElement + direction) % 7;\r
820             switch(currentElement) {\r
821                 case 0:\r
822                   h = hwndMain; break; // passing this case always makes the loop exit\r
823                 case 1:\r
824                   h = buttonDesc[0].hwnd; break; // could be NULL\r
825                 case 2:\r
826                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
827                   h = e1; break;\r
828                 case 3:\r
829                   if(!EngineOutputIsUp()) continue;\r
830                   h = e2; break;\r
831                 case 4:\r
832                   if(!MoveHistoryIsUp()) continue;\r
833                   h = mh; break;\r
834 //              case 6: // input to eval graph does not seem to get here!\r
835 //                if(!EvalGraphIsUp()) continue;\r
836 //                h = evalGraphDialog; break;\r
837                 case 5:\r
838                   if(!appData.icsActive) continue;\r
839                   SAY("display");\r
840                   h = hText; break;\r
841                 case 6:\r
842                   if(!appData.icsActive) continue;\r
843                   SAY("input");\r
844                   h = hInput; break;\r
845             }\r
846           } while(h == 0);\r
847 \r
848           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
849           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
850           SetFocus(h);\r
851 \r
852           continue; // this message now has been processed\r
853         }\r
854       }\r
855 \r
856       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
857           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
858           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
859           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
860           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
861           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
862           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
863           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
864           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
865           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
866         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
867         for(i=0; i<MAX_CHAT; i++) \r
868             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
869                 done = 1; break;\r
870         }\r
871         if(done) continue; // [HGM] chat: end patch\r
872         TranslateMessage(&msg); /* Translates virtual key codes */\r
873         DispatchMessage(&msg);  /* Dispatches message to window */\r
874       }\r
875     }\r
876 \r
877 \r
878   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
879 }\r
880 \r
881 /*---------------------------------------------------------------------------*\\r
882  *\r
883  * Initialization functions\r
884  *\r
885 \*---------------------------------------------------------------------------*/\r
886 \r
887 void\r
888 SetUserLogo()\r
889 {   // update user logo if necessary\r
890     static char oldUserName[MSG_SIZ], *curName;\r
891 \r
892     if(appData.autoLogo) {\r
893           curName = UserName();\r
894           if(strcmp(curName, oldUserName)) {\r
895             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
896                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
897                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
898                 if(userLogo == NULL)\r
899                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
900           }\r
901     }\r
902 }\r
903 \r
904 BOOL\r
905 InitApplication(HINSTANCE hInstance)\r
906 {\r
907   WNDCLASS wc;\r
908 \r
909   /* Fill in window class structure with parameters that describe the */\r
910   /* main window. */\r
911 \r
912   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
913   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
914   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
915   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
916   wc.hInstance     = hInstance;         /* Owner of this class */\r
917   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
918   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
919   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
920   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
921   wc.lpszClassName = szAppName;                 /* Name to register as */\r
922 \r
923   /* Register the window class and return success/failure code. */\r
924   if (!RegisterClass(&wc)) return FALSE;\r
925 \r
926   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
927   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
928   wc.cbClsExtra    = 0;\r
929   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
930   wc.hInstance     = hInstance;\r
931   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
932   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
933   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
934   wc.lpszMenuName  = NULL;\r
935   wc.lpszClassName = szConsoleName;\r
936 \r
937   if (!RegisterClass(&wc)) return FALSE;\r
938   return TRUE;\r
939 }\r
940 \r
941 \r
942 /* Set by InitInstance, used by EnsureOnScreen */\r
943 int screenHeight, screenWidth;\r
944 \r
945 void\r
946 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
947 {\r
948 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
949   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
950   if (*x > screenWidth - 32) *x = 0;\r
951   if (*y > screenHeight - 32) *y = 0;\r
952   if (*x < minX) *x = minX;\r
953   if (*y < minY) *y = minY;\r
954 }\r
955 \r
956 BOOL\r
957 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
958 {\r
959   HWND hwnd; /* Main window handle. */\r
960   int ibs;\r
961   WINDOWPLACEMENT wp;\r
962   char *filepart;\r
963 \r
964   hInst = hInstance;    /* Store instance handle in our global variable */\r
965   programName = szAppName;\r
966 \r
967   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
968     *filepart = NULLCHAR;\r
969   } else {\r
970     GetCurrentDirectory(MSG_SIZ, installDir);\r
971   }\r
972   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
973   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
974   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
975   /* xboard, and older WinBoards, controlled the move sound with the\r
976      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
977      always turn the option on (so that the backend will call us),\r
978      then let the user turn the sound off by setting it to silence if\r
979      desired.  To accommodate old winboard.ini files saved by old\r
980      versions of WinBoard, we also turn off the sound if the option\r
981      was initially set to false. [HGM] taken out of InitAppData */\r
982   if (!appData.ringBellAfterMoves) {\r
983     sounds[(int)SoundMove].name = strdup("");\r
984     appData.ringBellAfterMoves = TRUE;\r
985   }\r
986   if (appData.debugMode) {\r
987     debugFP = fopen(appData.nameOfDebugFile, "w");\r
988     setbuf(debugFP, NULL);\r
989   }\r
990 \r
991   LoadLanguageFile(appData.language);\r
992 \r
993   InitBackEnd1();\r
994 \r
995 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
996 //  InitEngineUCI( installDir, &second );\r
997 \r
998   /* Create a main window for this application instance. */\r
999   hwnd = CreateWindow(szAppName, szTitle,\r
1000                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1001                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1002                       NULL, NULL, hInstance, NULL);\r
1003   hwndMain = hwnd;\r
1004 \r
1005   /* If window could not be created, return "failure" */\r
1006   if (!hwnd) {\r
1007     return (FALSE);\r
1008   }\r
1009 \r
1010   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1011   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
1012       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1013 \r
1014       if (first.programLogo == NULL && appData.debugMode) {\r
1015           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1016       }\r
1017   } else if(appData.autoLogo) {\r
1018       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1019         char buf[MSG_SIZ];\r
1020           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1021         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1022       }\r
1023   }\r
1024 \r
1025   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1026       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1027 \r
1028       if (second.programLogo == NULL && appData.debugMode) {\r
1029           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1030       }\r
1031   } else if(appData.autoLogo) {\r
1032       char buf[MSG_SIZ];\r
1033       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1034         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1035         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1036       } else\r
1037       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1038         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1039         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1040       }\r
1041   }\r
1042 \r
1043   SetUserLogo();\r
1044 \r
1045   iconWhite = LoadIcon(hInstance, "icon_white");\r
1046   iconBlack = LoadIcon(hInstance, "icon_black");\r
1047   iconCurrent = iconWhite;\r
1048   InitDrawingColors();\r
1049   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1050   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1051   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1052     /* Compute window size for each board size, and use the largest\r
1053        size that fits on this screen as the default. */\r
1054     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1055     if (boardSize == (BoardSize)-1 &&\r
1056         winH <= screenHeight\r
1057            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1058         && winW <= screenWidth) {\r
1059       boardSize = (BoardSize)ibs;\r
1060     }\r
1061   }\r
1062 \r
1063   InitDrawingSizes(boardSize, 0);\r
1064   InitMenuChecks();\r
1065   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1066 \r
1067   /* [AS] Load textures if specified */\r
1068   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1069   \r
1070   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1071       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1072       liteBackTextureMode = appData.liteBackTextureMode;\r
1073 \r
1074       if (liteBackTexture == NULL && appData.debugMode) {\r
1075           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1076       }\r
1077   }\r
1078   \r
1079   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1080       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1081       darkBackTextureMode = appData.darkBackTextureMode;\r
1082 \r
1083       if (darkBackTexture == NULL && appData.debugMode) {\r
1084           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1085       }\r
1086   }\r
1087 \r
1088   mysrandom( (unsigned) time(NULL) );\r
1089 \r
1090   /* [AS] Restore layout */\r
1091   if( wpMoveHistory.visible ) {\r
1092       MoveHistoryPopUp();\r
1093   }\r
1094 \r
1095   if( wpEvalGraph.visible ) {\r
1096       EvalGraphPopUp();\r
1097   }\r
1098 \r
1099   if( wpEngineOutput.visible ) {\r
1100       EngineOutputPopUp();\r
1101   }\r
1102 \r
1103   /* Make the window visible; update its client area; and return "success" */\r
1104   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1105   wp.length = sizeof(WINDOWPLACEMENT);\r
1106   wp.flags = 0;\r
1107   wp.showCmd = nCmdShow;\r
1108   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1109   wp.rcNormalPosition.left = wpMain.x;\r
1110   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1111   wp.rcNormalPosition.top = wpMain.y;\r
1112   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1113   SetWindowPlacement(hwndMain, &wp);\r
1114 \r
1115   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1116 \r
1117   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1118                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1119 \r
1120   if (hwndConsole) {\r
1121 #if AOT_CONSOLE\r
1122     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1123                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1124 #endif\r
1125     ShowWindow(hwndConsole, nCmdShow);\r
1126     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1127       char buf[MSG_SIZ], *p = buf, *q;\r
1128         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1129       do {\r
1130         q = strchr(p, ';');\r
1131         if(q) *q++ = 0;\r
1132         if(*p) ChatPopUp(p);\r
1133       } while(p=q);\r
1134     }\r
1135     SetActiveWindow(hwndConsole);\r
1136   }\r
1137   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1138   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1139 \r
1140   return TRUE;\r
1141 \r
1142 }\r
1143 \r
1144 VOID\r
1145 InitMenuChecks()\r
1146 {\r
1147   HMENU hmenu = GetMenu(hwndMain);\r
1148 \r
1149   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1150                         MF_BYCOMMAND|((appData.icsActive &&\r
1151                                        *appData.icsCommPort != NULLCHAR) ?\r
1152                                       MF_ENABLED : MF_GRAYED));\r
1153   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1154                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1155                                      MF_CHECKED : MF_UNCHECKED));\r
1156 }\r
1157 \r
1158 //---------------------------------------------------------------------------------------------------------\r
1159 \r
1160 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1161 #define XBOARD FALSE\r
1162 \r
1163 #define OPTCHAR "/"\r
1164 #define SEPCHAR "="\r
1165 \r
1166 #include "args.h"\r
1167 \r
1168 // front-end part of option handling\r
1169 \r
1170 VOID\r
1171 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1172 {\r
1173   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1174   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1175   DeleteDC(hdc);\r
1176   lf->lfWidth = 0;\r
1177   lf->lfEscapement = 0;\r
1178   lf->lfOrientation = 0;\r
1179   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1180   lf->lfItalic = mfp->italic;\r
1181   lf->lfUnderline = mfp->underline;\r
1182   lf->lfStrikeOut = mfp->strikeout;\r
1183   lf->lfCharSet = mfp->charset;\r
1184   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1185   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1186   lf->lfQuality = DEFAULT_QUALITY;\r
1187   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1188     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1189 }\r
1190 \r
1191 void\r
1192 CreateFontInMF(MyFont *mf)\r
1193\r
1194   LFfromMFP(&mf->lf, &mf->mfp);\r
1195   if (mf->hf) DeleteObject(mf->hf);\r
1196   mf->hf = CreateFontIndirect(&mf->lf);\r
1197 }\r
1198 \r
1199 // [HGM] This platform-dependent table provides the location for storing the color info\r
1200 void *\r
1201 colorVariable[] = {\r
1202   &whitePieceColor, \r
1203   &blackPieceColor, \r
1204   &lightSquareColor,\r
1205   &darkSquareColor, \r
1206   &highlightSquareColor,\r
1207   &premoveHighlightColor,\r
1208   NULL,\r
1209   &consoleBackgroundColor,\r
1210   &appData.fontForeColorWhite,\r
1211   &appData.fontBackColorWhite,\r
1212   &appData.fontForeColorBlack,\r
1213   &appData.fontBackColorBlack,\r
1214   &appData.evalHistColorWhite,\r
1215   &appData.evalHistColorBlack,\r
1216   &appData.highlightArrowColor,\r
1217 };\r
1218 \r
1219 /* Command line font name parser.  NULL name means do nothing.\r
1220    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1221    For backward compatibility, syntax without the colon is also\r
1222    accepted, but font names with digits in them won't work in that case.\r
1223 */\r
1224 VOID\r
1225 ParseFontName(char *name, MyFontParams *mfp)\r
1226 {\r
1227   char *p, *q;\r
1228   if (name == NULL) return;\r
1229   p = name;\r
1230   q = strchr(p, ':');\r
1231   if (q) {\r
1232     if (q - p >= sizeof(mfp->faceName))\r
1233       ExitArgError(_("Font name too long:"), name);\r
1234     memcpy(mfp->faceName, p, q - p);\r
1235     mfp->faceName[q - p] = NULLCHAR;\r
1236     p = q + 1;\r
1237   } else {\r
1238     q = mfp->faceName;\r
1239     while (*p && !isdigit(*p)) {\r
1240       *q++ = *p++;\r
1241       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1242         ExitArgError(_("Font name too long:"), name);\r
1243     }\r
1244     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1245     *q = NULLCHAR;\r
1246   }\r
1247   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1248   mfp->pointSize = (float) atof(p);\r
1249   mfp->bold = (strchr(p, 'b') != NULL);\r
1250   mfp->italic = (strchr(p, 'i') != NULL);\r
1251   mfp->underline = (strchr(p, 'u') != NULL);\r
1252   mfp->strikeout = (strchr(p, 's') != NULL);\r
1253   mfp->charset = DEFAULT_CHARSET;\r
1254   q = strchr(p, 'c');\r
1255   if (q)\r
1256     mfp->charset = (BYTE) atoi(q+1);\r
1257 }\r
1258 \r
1259 void\r
1260 ParseFont(char *name, int number)\r
1261 { // wrapper to shield back-end from 'font'\r
1262   ParseFontName(name, &font[boardSize][number]->mfp);\r
1263 }\r
1264 \r
1265 void\r
1266 SetFontDefaults()\r
1267 { // in WB  we have a 2D array of fonts; this initializes their description\r
1268   int i, j;\r
1269   /* Point font array elements to structures and\r
1270      parse default font names */\r
1271   for (i=0; i<NUM_FONTS; i++) {\r
1272     for (j=0; j<NUM_SIZES; j++) {\r
1273       font[j][i] = &fontRec[j][i];\r
1274       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1275     }\r
1276   }\r
1277 }\r
1278 \r
1279 void\r
1280 CreateFonts()\r
1281 { // here we create the actual fonts from the selected descriptions\r
1282   int i, j;\r
1283   for (i=0; i<NUM_FONTS; i++) {\r
1284     for (j=0; j<NUM_SIZES; j++) {\r
1285       CreateFontInMF(font[j][i]);\r
1286     }\r
1287   }\r
1288 }\r
1289 /* Color name parser.\r
1290    X version accepts X color names, but this one\r
1291    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1292 COLORREF\r
1293 ParseColorName(char *name)\r
1294 {\r
1295   int red, green, blue, count;\r
1296   char buf[MSG_SIZ];\r
1297 \r
1298   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1299   if (count != 3) {\r
1300     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1301       &red, &green, &blue);\r
1302   }\r
1303   if (count != 3) {\r
1304     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1305     DisplayError(buf, 0);\r
1306     return RGB(0, 0, 0);\r
1307   }\r
1308   return PALETTERGB(red, green, blue);\r
1309 }\r
1310 \r
1311 void\r
1312 ParseColor(int n, char *name)\r
1313 { // for WinBoard the color is an int, which needs to be derived from the string\r
1314   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1315 }\r
1316 \r
1317 void\r
1318 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1319 {\r
1320   char *e = argValue;\r
1321   int eff = 0;\r
1322 \r
1323   while (*e) {\r
1324     if (*e == 'b')      eff |= CFE_BOLD;\r
1325     else if (*e == 'i') eff |= CFE_ITALIC;\r
1326     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1327     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1328     else if (*e == '#' || isdigit(*e)) break;\r
1329     e++;\r
1330   }\r
1331   *effects = eff;\r
1332   *color   = ParseColorName(e);\r
1333 }\r
1334 \r
1335 void\r
1336 ParseTextAttribs(ColorClass cc, char *s)\r
1337 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1338     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1339     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1340 }\r
1341 \r
1342 void\r
1343 ParseBoardSize(void *addr, char *name)\r
1344 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1345   BoardSize bs = SizeTiny;\r
1346   while (sizeInfo[bs].name != NULL) {\r
1347     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1348         *(BoardSize *)addr = bs;\r
1349         return;\r
1350     }\r
1351     bs++;\r
1352   }\r
1353   ExitArgError(_("Unrecognized board size value"), name);\r
1354 }\r
1355 \r
1356 void\r
1357 LoadAllSounds()\r
1358 { // [HGM] import name from appData first\r
1359   ColorClass cc;\r
1360   SoundClass sc;\r
1361   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1362     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1363     textAttribs[cc].sound.data = NULL;\r
1364     MyLoadSound(&textAttribs[cc].sound);\r
1365   }\r
1366   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1367     textAttribs[cc].sound.name = strdup("");\r
1368     textAttribs[cc].sound.data = NULL;\r
1369   }\r
1370   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1371     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1372     sounds[sc].data = NULL;\r
1373     MyLoadSound(&sounds[sc]);\r
1374   }\r
1375 }\r
1376 \r
1377 void\r
1378 SetCommPortDefaults()\r
1379 {\r
1380    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1381   dcb.DCBlength = sizeof(DCB);\r
1382   dcb.BaudRate = 9600;\r
1383   dcb.fBinary = TRUE;\r
1384   dcb.fParity = FALSE;\r
1385   dcb.fOutxCtsFlow = FALSE;\r
1386   dcb.fOutxDsrFlow = FALSE;\r
1387   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1388   dcb.fDsrSensitivity = FALSE;\r
1389   dcb.fTXContinueOnXoff = TRUE;\r
1390   dcb.fOutX = FALSE;\r
1391   dcb.fInX = FALSE;\r
1392   dcb.fNull = FALSE;\r
1393   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1394   dcb.fAbortOnError = FALSE;\r
1395   dcb.ByteSize = 7;\r
1396   dcb.Parity = SPACEPARITY;\r
1397   dcb.StopBits = ONESTOPBIT;\r
1398 }\r
1399 \r
1400 // [HGM] args: these three cases taken out to stay in front-end\r
1401 void\r
1402 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1403 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1404         // while the curent board size determines the element. This system should be ported to XBoard.\r
1405         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1406         int bs;\r
1407         for (bs=0; bs<NUM_SIZES; bs++) {\r
1408           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1409           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1410           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1411             ad->argName, mfp->faceName, mfp->pointSize,\r
1412             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1413             mfp->bold ? "b" : "",\r
1414             mfp->italic ? "i" : "",\r
1415             mfp->underline ? "u" : "",\r
1416             mfp->strikeout ? "s" : "",\r
1417             (int)mfp->charset);\r
1418         }\r
1419       }\r
1420 \r
1421 void\r
1422 ExportSounds()\r
1423 { // [HGM] copy the names from the internal WB variables to appData\r
1424   ColorClass cc;\r
1425   SoundClass sc;\r
1426   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1427     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1428   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1429     (&appData.soundMove)[sc] = sounds[sc].name;\r
1430 }\r
1431 \r
1432 void\r
1433 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1434 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1435         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1436         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1437           (ta->effects & CFE_BOLD) ? "b" : "",\r
1438           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1439           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1440           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1441           (ta->effects) ? " " : "",\r
1442           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1443       }\r
1444 \r
1445 void\r
1446 SaveColor(FILE *f, ArgDescriptor *ad)\r
1447 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1448         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1449         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1450           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1451 }\r
1452 \r
1453 void\r
1454 SaveBoardSize(FILE *f, char *name, void *addr)\r
1455 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1456   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1457 }\r
1458 \r
1459 void\r
1460 ParseCommPortSettings(char *s)\r
1461 { // wrapper to keep dcb from back-end\r
1462   ParseCommSettings(s, &dcb);\r
1463 }\r
1464 \r
1465 void\r
1466 GetWindowCoords()\r
1467 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1468   GetActualPlacement(hwndMain, &wpMain);\r
1469   GetActualPlacement(hwndConsole, &wpConsole);\r
1470   GetActualPlacement(commentDialog, &wpComment);\r
1471   GetActualPlacement(editTagsDialog, &wpTags);\r
1472   GetActualPlacement(gameListDialog, &wpGameList);\r
1473   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1474   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1475   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1476 }\r
1477 \r
1478 void\r
1479 PrintCommPortSettings(FILE *f, char *name)\r
1480 { // wrapper to shield back-end from DCB\r
1481       PrintCommSettings(f, name, &dcb);\r
1482 }\r
1483 \r
1484 int\r
1485 MySearchPath(char *installDir, char *name, char *fullname)\r
1486 {\r
1487   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1488   if(name[0]== '%') {\r
1489     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1490     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1491       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1492       *strchr(buf, '%') = 0;\r
1493       strcat(fullname, getenv(buf));\r
1494       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1495     }\r
1496     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1497     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1498     return (int) strlen(fullname);\r
1499   }\r
1500   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1501 }\r
1502 \r
1503 int\r
1504 MyGetFullPathName(char *name, char *fullname)\r
1505 {\r
1506   char *dummy;\r
1507   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1508 }\r
1509 \r
1510 int\r
1511 MainWindowUp()\r
1512 { // [HGM] args: allows testing if main window is realized from back-end\r
1513   return hwndMain != NULL;\r
1514 }\r
1515 \r
1516 void\r
1517 PopUpStartupDialog()\r
1518 {\r
1519     FARPROC lpProc;\r
1520     \r
1521     LoadLanguageFile(appData.language);\r
1522     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1523     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1524     FreeProcInstance(lpProc);\r
1525 }\r
1526 \r
1527 /*---------------------------------------------------------------------------*\\r
1528  *\r
1529  * GDI board drawing routines\r
1530  *\r
1531 \*---------------------------------------------------------------------------*/\r
1532 \r
1533 /* [AS] Draw square using background texture */\r
1534 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1535 {\r
1536     XFORM   x;\r
1537 \r
1538     if( mode == 0 ) {\r
1539         return; /* Should never happen! */\r
1540     }\r
1541 \r
1542     SetGraphicsMode( dst, GM_ADVANCED );\r
1543 \r
1544     switch( mode ) {\r
1545     case 1:\r
1546         /* Identity */\r
1547         break;\r
1548     case 2:\r
1549         /* X reflection */\r
1550         x.eM11 = -1.0;\r
1551         x.eM12 = 0;\r
1552         x.eM21 = 0;\r
1553         x.eM22 = 1.0;\r
1554         x.eDx = (FLOAT) dw + dx - 1;\r
1555         x.eDy = 0;\r
1556         dx = 0;\r
1557         SetWorldTransform( dst, &x );\r
1558         break;\r
1559     case 3:\r
1560         /* Y reflection */\r
1561         x.eM11 = 1.0;\r
1562         x.eM12 = 0;\r
1563         x.eM21 = 0;\r
1564         x.eM22 = -1.0;\r
1565         x.eDx = 0;\r
1566         x.eDy = (FLOAT) dh + dy - 1;\r
1567         dy = 0;\r
1568         SetWorldTransform( dst, &x );\r
1569         break;\r
1570     case 4:\r
1571         /* X/Y flip */\r
1572         x.eM11 = 0;\r
1573         x.eM12 = 1.0;\r
1574         x.eM21 = 1.0;\r
1575         x.eM22 = 0;\r
1576         x.eDx = (FLOAT) dx;\r
1577         x.eDy = (FLOAT) dy;\r
1578         dx = 0;\r
1579         dy = 0;\r
1580         SetWorldTransform( dst, &x );\r
1581         break;\r
1582     }\r
1583 \r
1584     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1585 \r
1586     x.eM11 = 1.0;\r
1587     x.eM12 = 0;\r
1588     x.eM21 = 0;\r
1589     x.eM22 = 1.0;\r
1590     x.eDx = 0;\r
1591     x.eDy = 0;\r
1592     SetWorldTransform( dst, &x );\r
1593 \r
1594     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1595 }\r
1596 \r
1597 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1598 enum {\r
1599     PM_WP = (int) WhitePawn, \r
1600     PM_WN = (int) WhiteKnight, \r
1601     PM_WB = (int) WhiteBishop, \r
1602     PM_WR = (int) WhiteRook, \r
1603     PM_WQ = (int) WhiteQueen, \r
1604     PM_WF = (int) WhiteFerz, \r
1605     PM_WW = (int) WhiteWazir, \r
1606     PM_WE = (int) WhiteAlfil, \r
1607     PM_WM = (int) WhiteMan, \r
1608     PM_WO = (int) WhiteCannon, \r
1609     PM_WU = (int) WhiteUnicorn, \r
1610     PM_WH = (int) WhiteNightrider, \r
1611     PM_WA = (int) WhiteAngel, \r
1612     PM_WC = (int) WhiteMarshall, \r
1613     PM_WAB = (int) WhiteCardinal, \r
1614     PM_WD = (int) WhiteDragon, \r
1615     PM_WL = (int) WhiteLance, \r
1616     PM_WS = (int) WhiteCobra, \r
1617     PM_WV = (int) WhiteFalcon, \r
1618     PM_WSG = (int) WhiteSilver, \r
1619     PM_WG = (int) WhiteGrasshopper, \r
1620     PM_WK = (int) WhiteKing,\r
1621     PM_BP = (int) BlackPawn, \r
1622     PM_BN = (int) BlackKnight, \r
1623     PM_BB = (int) BlackBishop, \r
1624     PM_BR = (int) BlackRook, \r
1625     PM_BQ = (int) BlackQueen, \r
1626     PM_BF = (int) BlackFerz, \r
1627     PM_BW = (int) BlackWazir, \r
1628     PM_BE = (int) BlackAlfil, \r
1629     PM_BM = (int) BlackMan,\r
1630     PM_BO = (int) BlackCannon, \r
1631     PM_BU = (int) BlackUnicorn, \r
1632     PM_BH = (int) BlackNightrider, \r
1633     PM_BA = (int) BlackAngel, \r
1634     PM_BC = (int) BlackMarshall, \r
1635     PM_BG = (int) BlackGrasshopper, \r
1636     PM_BAB = (int) BlackCardinal,\r
1637     PM_BD = (int) BlackDragon,\r
1638     PM_BL = (int) BlackLance,\r
1639     PM_BS = (int) BlackCobra,\r
1640     PM_BV = (int) BlackFalcon,\r
1641     PM_BSG = (int) BlackSilver,\r
1642     PM_BK = (int) BlackKing\r
1643 };\r
1644 \r
1645 static HFONT hPieceFont = NULL;\r
1646 static HBITMAP hPieceMask[(int) EmptySquare];\r
1647 static HBITMAP hPieceFace[(int) EmptySquare];\r
1648 static int fontBitmapSquareSize = 0;\r
1649 static char pieceToFontChar[(int) EmptySquare] =\r
1650                               { 'p', 'n', 'b', 'r', 'q', \r
1651                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1652                       'k', 'o', 'm', 'v', 't', 'w', \r
1653                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1654                                                               'l' };\r
1655 \r
1656 extern BOOL SetCharTable( char *table, const char * map );\r
1657 /* [HGM] moved to backend.c */\r
1658 \r
1659 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1660 {\r
1661     HBRUSH hbrush;\r
1662     BYTE r1 = GetRValue( color );\r
1663     BYTE g1 = GetGValue( color );\r
1664     BYTE b1 = GetBValue( color );\r
1665     BYTE r2 = r1 / 2;\r
1666     BYTE g2 = g1 / 2;\r
1667     BYTE b2 = b1 / 2;\r
1668     RECT rc;\r
1669 \r
1670     /* Create a uniform background first */\r
1671     hbrush = CreateSolidBrush( color );\r
1672     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1673     FillRect( hdc, &rc, hbrush );\r
1674     DeleteObject( hbrush );\r
1675     \r
1676     if( mode == 1 ) {\r
1677         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1678         int steps = squareSize / 2;\r
1679         int i;\r
1680 \r
1681         for( i=0; i<steps; i++ ) {\r
1682             BYTE r = r1 - (r1-r2) * i / steps;\r
1683             BYTE g = g1 - (g1-g2) * i / steps;\r
1684             BYTE b = b1 - (b1-b2) * i / steps;\r
1685 \r
1686             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1687             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1688             FillRect( hdc, &rc, hbrush );\r
1689             DeleteObject(hbrush);\r
1690         }\r
1691     }\r
1692     else if( mode == 2 ) {\r
1693         /* Diagonal gradient, good more or less for every piece */\r
1694         POINT triangle[3];\r
1695         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1696         HBRUSH hbrush_old;\r
1697         int steps = squareSize;\r
1698         int i;\r
1699 \r
1700         triangle[0].x = squareSize - steps;\r
1701         triangle[0].y = squareSize;\r
1702         triangle[1].x = squareSize;\r
1703         triangle[1].y = squareSize;\r
1704         triangle[2].x = squareSize;\r
1705         triangle[2].y = squareSize - steps;\r
1706 \r
1707         for( i=0; i<steps; i++ ) {\r
1708             BYTE r = r1 - (r1-r2) * i / steps;\r
1709             BYTE g = g1 - (g1-g2) * i / steps;\r
1710             BYTE b = b1 - (b1-b2) * i / steps;\r
1711 \r
1712             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1713             hbrush_old = SelectObject( hdc, hbrush );\r
1714             Polygon( hdc, triangle, 3 );\r
1715             SelectObject( hdc, hbrush_old );\r
1716             DeleteObject(hbrush);\r
1717             triangle[0].x++;\r
1718             triangle[2].y++;\r
1719         }\r
1720 \r
1721         SelectObject( hdc, hpen );\r
1722     }\r
1723 }\r
1724 \r
1725 /*\r
1726     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1727     seems to work ok. The main problem here is to find the "inside" of a chess\r
1728     piece: follow the steps as explained below.\r
1729 */\r
1730 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1731 {\r
1732     HBITMAP hbm;\r
1733     HBITMAP hbm_old;\r
1734     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1735     RECT rc;\r
1736     SIZE sz;\r
1737     POINT pt;\r
1738     int backColor = whitePieceColor; \r
1739     int foreColor = blackPieceColor;\r
1740     \r
1741     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1742         backColor = appData.fontBackColorWhite;\r
1743         foreColor = appData.fontForeColorWhite;\r
1744     }\r
1745     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1746         backColor = appData.fontBackColorBlack;\r
1747         foreColor = appData.fontForeColorBlack;\r
1748     }\r
1749 \r
1750     /* Mask */\r
1751     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1752 \r
1753     hbm_old = SelectObject( hdc, hbm );\r
1754 \r
1755     rc.left = 0;\r
1756     rc.top = 0;\r
1757     rc.right = squareSize;\r
1758     rc.bottom = squareSize;\r
1759 \r
1760     /* Step 1: background is now black */\r
1761     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1762 \r
1763     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1764 \r
1765     pt.x = (squareSize - sz.cx) / 2;\r
1766     pt.y = (squareSize - sz.cy) / 2;\r
1767 \r
1768     SetBkMode( hdc, TRANSPARENT );\r
1769     SetTextColor( hdc, chroma );\r
1770     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1771     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1772 \r
1773     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1774     /* Step 3: the area outside the piece is filled with white */\r
1775 //    FloodFill( hdc, 0, 0, chroma );\r
1776     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1777     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1778     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1779     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1780     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1781     /* \r
1782         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1783         but if the start point is not inside the piece we're lost!\r
1784         There should be a better way to do this... if we could create a region or path\r
1785         from the fill operation we would be fine for example.\r
1786     */\r
1787 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1788     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1789 \r
1790     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1791         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1792         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1793 \r
1794         SelectObject( dc2, bm2 );\r
1795         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1796         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1797         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1798         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1799         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1800 \r
1801         DeleteDC( dc2 );\r
1802         DeleteObject( bm2 );\r
1803     }\r
1804 \r
1805     SetTextColor( hdc, 0 );\r
1806     /* \r
1807         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1808         draw the piece again in black for safety.\r
1809     */\r
1810     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1811 \r
1812     SelectObject( hdc, hbm_old );\r
1813 \r
1814     if( hPieceMask[index] != NULL ) {\r
1815         DeleteObject( hPieceMask[index] );\r
1816     }\r
1817 \r
1818     hPieceMask[index] = hbm;\r
1819 \r
1820     /* Face */\r
1821     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1822 \r
1823     SelectObject( hdc, hbm );\r
1824 \r
1825     {\r
1826         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1827         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1828         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1829 \r
1830         SelectObject( dc1, hPieceMask[index] );\r
1831         SelectObject( dc2, bm2 );\r
1832         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1833         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1834         \r
1835         /* \r
1836             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1837             the piece background and deletes (makes transparent) the rest.\r
1838             Thanks to that mask, we are free to paint the background with the greates\r
1839             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1840             We use this, to make gradients and give the pieces a "roundish" look.\r
1841         */\r
1842         SetPieceBackground( hdc, backColor, 2 );\r
1843         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1844 \r
1845         DeleteDC( dc2 );\r
1846         DeleteDC( dc1 );\r
1847         DeleteObject( bm2 );\r
1848     }\r
1849 \r
1850     SetTextColor( hdc, foreColor );\r
1851     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1852 \r
1853     SelectObject( hdc, hbm_old );\r
1854 \r
1855     if( hPieceFace[index] != NULL ) {\r
1856         DeleteObject( hPieceFace[index] );\r
1857     }\r
1858 \r
1859     hPieceFace[index] = hbm;\r
1860 }\r
1861 \r
1862 static int TranslatePieceToFontPiece( int piece )\r
1863 {\r
1864     switch( piece ) {\r
1865     case BlackPawn:\r
1866         return PM_BP;\r
1867     case BlackKnight:\r
1868         return PM_BN;\r
1869     case BlackBishop:\r
1870         return PM_BB;\r
1871     case BlackRook:\r
1872         return PM_BR;\r
1873     case BlackQueen:\r
1874         return PM_BQ;\r
1875     case BlackKing:\r
1876         return PM_BK;\r
1877     case WhitePawn:\r
1878         return PM_WP;\r
1879     case WhiteKnight:\r
1880         return PM_WN;\r
1881     case WhiteBishop:\r
1882         return PM_WB;\r
1883     case WhiteRook:\r
1884         return PM_WR;\r
1885     case WhiteQueen:\r
1886         return PM_WQ;\r
1887     case WhiteKing:\r
1888         return PM_WK;\r
1889 \r
1890     case BlackAngel:\r
1891         return PM_BA;\r
1892     case BlackMarshall:\r
1893         return PM_BC;\r
1894     case BlackFerz:\r
1895         return PM_BF;\r
1896     case BlackNightrider:\r
1897         return PM_BH;\r
1898     case BlackAlfil:\r
1899         return PM_BE;\r
1900     case BlackWazir:\r
1901         return PM_BW;\r
1902     case BlackUnicorn:\r
1903         return PM_BU;\r
1904     case BlackCannon:\r
1905         return PM_BO;\r
1906     case BlackGrasshopper:\r
1907         return PM_BG;\r
1908     case BlackMan:\r
1909         return PM_BM;\r
1910     case BlackSilver:\r
1911         return PM_BSG;\r
1912     case BlackLance:\r
1913         return PM_BL;\r
1914     case BlackFalcon:\r
1915         return PM_BV;\r
1916     case BlackCobra:\r
1917         return PM_BS;\r
1918     case BlackCardinal:\r
1919         return PM_BAB;\r
1920     case BlackDragon:\r
1921         return PM_BD;\r
1922 \r
1923     case WhiteAngel:\r
1924         return PM_WA;\r
1925     case WhiteMarshall:\r
1926         return PM_WC;\r
1927     case WhiteFerz:\r
1928         return PM_WF;\r
1929     case WhiteNightrider:\r
1930         return PM_WH;\r
1931     case WhiteAlfil:\r
1932         return PM_WE;\r
1933     case WhiteWazir:\r
1934         return PM_WW;\r
1935     case WhiteUnicorn:\r
1936         return PM_WU;\r
1937     case WhiteCannon:\r
1938         return PM_WO;\r
1939     case WhiteGrasshopper:\r
1940         return PM_WG;\r
1941     case WhiteMan:\r
1942         return PM_WM;\r
1943     case WhiteSilver:\r
1944         return PM_WSG;\r
1945     case WhiteLance:\r
1946         return PM_WL;\r
1947     case WhiteFalcon:\r
1948         return PM_WV;\r
1949     case WhiteCobra:\r
1950         return PM_WS;\r
1951     case WhiteCardinal:\r
1952         return PM_WAB;\r
1953     case WhiteDragon:\r
1954         return PM_WD;\r
1955     }\r
1956 \r
1957     return 0;\r
1958 }\r
1959 \r
1960 void CreatePiecesFromFont()\r
1961 {\r
1962     LOGFONT lf;\r
1963     HDC hdc_window = NULL;\r
1964     HDC hdc = NULL;\r
1965     HFONT hfont_old;\r
1966     int fontHeight;\r
1967     int i;\r
1968 \r
1969     if( fontBitmapSquareSize < 0 ) {\r
1970         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1971         return;\r
1972     }\r
1973 \r
1974     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1975         fontBitmapSquareSize = -1;\r
1976         return;\r
1977     }\r
1978 \r
1979     if( fontBitmapSquareSize != squareSize ) {\r
1980         hdc_window = GetDC( hwndMain );\r
1981         hdc = CreateCompatibleDC( hdc_window );\r
1982 \r
1983         if( hPieceFont != NULL ) {\r
1984             DeleteObject( hPieceFont );\r
1985         }\r
1986         else {\r
1987             for( i=0; i<=(int)BlackKing; i++ ) {\r
1988                 hPieceMask[i] = NULL;\r
1989                 hPieceFace[i] = NULL;\r
1990             }\r
1991         }\r
1992 \r
1993         fontHeight = 75;\r
1994 \r
1995         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1996             fontHeight = appData.fontPieceSize;\r
1997         }\r
1998 \r
1999         fontHeight = (fontHeight * squareSize) / 100;\r
2000 \r
2001         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2002         lf.lfWidth = 0;\r
2003         lf.lfEscapement = 0;\r
2004         lf.lfOrientation = 0;\r
2005         lf.lfWeight = FW_NORMAL;\r
2006         lf.lfItalic = 0;\r
2007         lf.lfUnderline = 0;\r
2008         lf.lfStrikeOut = 0;\r
2009         lf.lfCharSet = DEFAULT_CHARSET;\r
2010         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2011         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2012         lf.lfQuality = PROOF_QUALITY;\r
2013         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2014         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2015         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2016 \r
2017         hPieceFont = CreateFontIndirect( &lf );\r
2018 \r
2019         if( hPieceFont == NULL ) {\r
2020             fontBitmapSquareSize = -2;\r
2021         }\r
2022         else {\r
2023             /* Setup font-to-piece character table */\r
2024             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2025                 /* No (or wrong) global settings, try to detect the font */\r
2026                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2027                     /* Alpha */\r
2028                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2029                 }\r
2030                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2031                     /* DiagramTT* family */\r
2032                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2033                 }\r
2034                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2035                     /* Fairy symbols */\r
2036                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2037                 }\r
2038                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2039                     /* Good Companion (Some characters get warped as literal :-( */\r
2040                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2041                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2042                     SetCharTable(pieceToFontChar, s);\r
2043                 }\r
2044                 else {\r
2045                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2046                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2047                 }\r
2048             }\r
2049 \r
2050             /* Create bitmaps */\r
2051             hfont_old = SelectObject( hdc, hPieceFont );\r
2052             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2053                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2054                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2055 \r
2056             SelectObject( hdc, hfont_old );\r
2057 \r
2058             fontBitmapSquareSize = squareSize;\r
2059         }\r
2060     }\r
2061 \r
2062     if( hdc != NULL ) {\r
2063         DeleteDC( hdc );\r
2064     }\r
2065 \r
2066     if( hdc_window != NULL ) {\r
2067         ReleaseDC( hwndMain, hdc_window );\r
2068     }\r
2069 }\r
2070 \r
2071 HBITMAP\r
2072 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2073 {\r
2074   char name[128];\r
2075 \r
2076     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2077   if (gameInfo.event &&\r
2078       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2079       strcmp(name, "k80s") == 0) {\r
2080     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2081   }\r
2082   return LoadBitmap(hinst, name);\r
2083 }\r
2084 \r
2085 \r
2086 /* Insert a color into the program's logical palette\r
2087    structure.  This code assumes the given color is\r
2088    the result of the RGB or PALETTERGB macro, and it\r
2089    knows how those macros work (which is documented).\r
2090 */\r
2091 VOID\r
2092 InsertInPalette(COLORREF color)\r
2093 {\r
2094   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2095 \r
2096   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2097     DisplayFatalError(_("Too many colors"), 0, 1);\r
2098     pLogPal->palNumEntries--;\r
2099     return;\r
2100   }\r
2101 \r
2102   pe->peFlags = (char) 0;\r
2103   pe->peRed = (char) (0xFF & color);\r
2104   pe->peGreen = (char) (0xFF & (color >> 8));\r
2105   pe->peBlue = (char) (0xFF & (color >> 16));\r
2106   return;\r
2107 }\r
2108 \r
2109 \r
2110 VOID\r
2111 InitDrawingColors()\r
2112 {\r
2113   if (pLogPal == NULL) {\r
2114     /* Allocate enough memory for a logical palette with\r
2115      * PALETTESIZE entries and set the size and version fields\r
2116      * of the logical palette structure.\r
2117      */\r
2118     pLogPal = (NPLOGPALETTE)\r
2119       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2120                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2121     pLogPal->palVersion    = 0x300;\r
2122   }\r
2123   pLogPal->palNumEntries = 0;\r
2124 \r
2125   InsertInPalette(lightSquareColor);\r
2126   InsertInPalette(darkSquareColor);\r
2127   InsertInPalette(whitePieceColor);\r
2128   InsertInPalette(blackPieceColor);\r
2129   InsertInPalette(highlightSquareColor);\r
2130   InsertInPalette(premoveHighlightColor);\r
2131 \r
2132   /*  create a logical color palette according the information\r
2133    *  in the LOGPALETTE structure.\r
2134    */\r
2135   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2136 \r
2137   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2138   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2139   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2140   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2141   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2142   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2143   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2144   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2145   /* [AS] Force rendering of the font-based pieces */\r
2146   if( fontBitmapSquareSize > 0 ) {\r
2147     fontBitmapSquareSize = 0;\r
2148   }\r
2149 }\r
2150 \r
2151 \r
2152 int\r
2153 BoardWidth(int boardSize, int n)\r
2154 { /* [HGM] argument n added to allow different width and height */\r
2155   int lineGap = sizeInfo[boardSize].lineGap;\r
2156 \r
2157   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2158       lineGap = appData.overrideLineGap;\r
2159   }\r
2160 \r
2161   return (n + 1) * lineGap +\r
2162           n * sizeInfo[boardSize].squareSize;\r
2163 }\r
2164 \r
2165 /* Respond to board resize by dragging edge */\r
2166 VOID\r
2167 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2168 {\r
2169   BoardSize newSize = NUM_SIZES - 1;\r
2170   static int recurse = 0;\r
2171   if (IsIconic(hwndMain)) return;\r
2172   if (recurse > 0) return;\r
2173   recurse++;\r
2174   while (newSize > 0) {\r
2175         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2176         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2177            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2178     newSize--;\r
2179   } \r
2180   boardSize = newSize;\r
2181   InitDrawingSizes(boardSize, flags);\r
2182   recurse--;\r
2183 }\r
2184 \r
2185 \r
2186 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2187 \r
2188 VOID\r
2189 InitDrawingSizes(BoardSize boardSize, int flags)\r
2190 {\r
2191   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2192   ChessSquare piece;\r
2193   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2194   HDC hdc;\r
2195   SIZE clockSize, messageSize;\r
2196   HFONT oldFont;\r
2197   char buf[MSG_SIZ];\r
2198   char *str;\r
2199   HMENU hmenu = GetMenu(hwndMain);\r
2200   RECT crect, wrect, oldRect;\r
2201   int offby;\r
2202   LOGBRUSH logbrush;\r
2203 \r
2204   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2205   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2206 \r
2207   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2208   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2209 \r
2210   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2211   oldRect.top = wpMain.y;\r
2212   oldRect.right = wpMain.x + wpMain.width;\r
2213   oldRect.bottom = wpMain.y + wpMain.height;\r
2214 \r
2215   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2216   smallLayout = sizeInfo[boardSize].smallLayout;\r
2217   squareSize = sizeInfo[boardSize].squareSize;\r
2218   lineGap = sizeInfo[boardSize].lineGap;\r
2219   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2220 \r
2221   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2222       lineGap = appData.overrideLineGap;\r
2223   }\r
2224 \r
2225   if (tinyLayout != oldTinyLayout) {\r
2226     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2227     if (tinyLayout) {\r
2228       style &= ~WS_SYSMENU;\r
2229       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2230                  "&Minimize\tCtrl+F4");\r
2231     } else {\r
2232       style |= WS_SYSMENU;\r
2233       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2234     }\r
2235     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2236 \r
2237     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2238       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2239         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2240     }\r
2241     DrawMenuBar(hwndMain);\r
2242   }\r
2243 \r
2244   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2245   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2246 \r
2247   /* Get text area sizes */\r
2248   hdc = GetDC(hwndMain);\r
2249   if (appData.clockMode) {\r
2250     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2251   } else {\r
2252     snprintf(buf, MSG_SIZ, _("White"));\r
2253   }\r
2254   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2255   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2256   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2257   str = _("We only care about the height here");\r
2258   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2259   SelectObject(hdc, oldFont);\r
2260   ReleaseDC(hwndMain, hdc);\r
2261 \r
2262   /* Compute where everything goes */\r
2263   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2264         /* [HGM] logo: if either logo is on, reserve space for it */\r
2265         logoHeight =  2*clockSize.cy;\r
2266         leftLogoRect.left   = OUTER_MARGIN;\r
2267         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2268         leftLogoRect.top    = OUTER_MARGIN;\r
2269         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2270 \r
2271         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2272         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2273         rightLogoRect.top    = OUTER_MARGIN;\r
2274         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2275 \r
2276 \r
2277     whiteRect.left = leftLogoRect.right;\r
2278     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2279     whiteRect.top = OUTER_MARGIN;\r
2280     whiteRect.bottom = whiteRect.top + logoHeight;\r
2281 \r
2282     blackRect.right = rightLogoRect.left;\r
2283     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2284     blackRect.top = whiteRect.top;\r
2285     blackRect.bottom = whiteRect.bottom;\r
2286   } else {\r
2287     whiteRect.left = OUTER_MARGIN;\r
2288     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2289     whiteRect.top = OUTER_MARGIN;\r
2290     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2291 \r
2292     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2293     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2294     blackRect.top = whiteRect.top;\r
2295     blackRect.bottom = whiteRect.bottom;\r
2296 \r
2297     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2298   }\r
2299 \r
2300   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2301   if (appData.showButtonBar) {\r
2302     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2303       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2304   } else {\r
2305     messageRect.right = OUTER_MARGIN + boardWidth;\r
2306   }\r
2307   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2308   messageRect.bottom = messageRect.top + messageSize.cy;\r
2309 \r
2310   boardRect.left = OUTER_MARGIN;\r
2311   boardRect.right = boardRect.left + boardWidth;\r
2312   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2313   boardRect.bottom = boardRect.top + boardHeight;\r
2314 \r
2315   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2316   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2317   oldBoardSize = boardSize;\r
2318   oldTinyLayout = tinyLayout;\r
2319   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2320   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2321     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2322   winW *= 1 + twoBoards;\r
2323   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2324   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2325   wpMain.height = winH; //       without disturbing window attachments\r
2326   GetWindowRect(hwndMain, &wrect);\r
2327   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2328                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2329 \r
2330   // [HGM] placement: let attached windows follow size change.\r
2331   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2332   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2333   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2334   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2335   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2336 \r
2337   /* compensate if menu bar wrapped */\r
2338   GetClientRect(hwndMain, &crect);\r
2339   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2340   wpMain.height += offby;\r
2341   switch (flags) {\r
2342   case WMSZ_TOPLEFT:\r
2343     SetWindowPos(hwndMain, NULL, \r
2344                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2345                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2346     break;\r
2347 \r
2348   case WMSZ_TOPRIGHT:\r
2349   case WMSZ_TOP:\r
2350     SetWindowPos(hwndMain, NULL, \r
2351                  wrect.left, wrect.bottom - wpMain.height, \r
2352                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2353     break;\r
2354 \r
2355   case WMSZ_BOTTOMLEFT:\r
2356   case WMSZ_LEFT:\r
2357     SetWindowPos(hwndMain, NULL, \r
2358                  wrect.right - wpMain.width, wrect.top, \r
2359                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2360     break;\r
2361 \r
2362   case WMSZ_BOTTOMRIGHT:\r
2363   case WMSZ_BOTTOM:\r
2364   case WMSZ_RIGHT:\r
2365   default:\r
2366     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2367                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2368     break;\r
2369   }\r
2370 \r
2371   hwndPause = NULL;\r
2372   for (i = 0; i < N_BUTTONS; i++) {\r
2373     if (buttonDesc[i].hwnd != NULL) {\r
2374       DestroyWindow(buttonDesc[i].hwnd);\r
2375       buttonDesc[i].hwnd = NULL;\r
2376     }\r
2377     if (appData.showButtonBar) {\r
2378       buttonDesc[i].hwnd =\r
2379         CreateWindow("BUTTON", buttonDesc[i].label,\r
2380                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2381                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2382                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2383                      (HMENU) buttonDesc[i].id,\r
2384                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2385       if (tinyLayout) {\r
2386         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2387                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2388                     MAKELPARAM(FALSE, 0));\r
2389       }\r
2390       if (buttonDesc[i].id == IDM_Pause)\r
2391         hwndPause = buttonDesc[i].hwnd;\r
2392       buttonDesc[i].wndproc = (WNDPROC)\r
2393         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2394     }\r
2395   }\r
2396   if (gridPen != NULL) DeleteObject(gridPen);\r
2397   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2398   if (premovePen != NULL) DeleteObject(premovePen);\r
2399   if (lineGap != 0) {\r
2400     logbrush.lbStyle = BS_SOLID;\r
2401     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2402     gridPen =\r
2403       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2404                    lineGap, &logbrush, 0, NULL);\r
2405     logbrush.lbColor = highlightSquareColor;\r
2406     highlightPen =\r
2407       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2408                    lineGap, &logbrush, 0, NULL);\r
2409 \r
2410     logbrush.lbColor = premoveHighlightColor; \r
2411     premovePen =\r
2412       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2413                    lineGap, &logbrush, 0, NULL);\r
2414 \r
2415     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2416     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2417       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2418       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2419         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2420       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2421         BOARD_WIDTH * (squareSize + lineGap);\r
2422       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2423     }\r
2424     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2425       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2426       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2427         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2428         lineGap / 2 + (i * (squareSize + lineGap));\r
2429       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2430         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2431       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2432     }\r
2433   }\r
2434 \r
2435   /* [HGM] Licensing requirement */\r
2436 #ifdef GOTHIC\r
2437   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2438 #endif\r
2439 #ifdef FALCON\r
2440   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2441 #endif\r
2442   GothicPopUp( "", VariantNormal);\r
2443 \r
2444 \r
2445 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2446 \r
2447   /* Load piece bitmaps for this board size */\r
2448   for (i=0; i<=2; i++) {\r
2449     for (piece = WhitePawn;\r
2450          (int) piece < (int) BlackPawn;\r
2451          piece = (ChessSquare) ((int) piece + 1)) {\r
2452       if (pieceBitmap[i][piece] != NULL)\r
2453         DeleteObject(pieceBitmap[i][piece]);\r
2454     }\r
2455   }\r
2456 \r
2457   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2458   // Orthodox Chess pieces\r
2459   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2460   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2461   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2462   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2463   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2464   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2465   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2466   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2467   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2468   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2469   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2470   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2471   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2472   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2473   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2474   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2475     // in Shogi, Hijack the unused Queen for Lance\r
2476     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2477     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2478     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2479   } else {\r
2480     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2481     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2482     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2483   }\r
2484 \r
2485   if(squareSize <= 72 && squareSize >= 33) { \r
2486     /* A & C are available in most sizes now */\r
2487     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2488       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2489       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2490       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2491       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2492       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2493       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2494       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2495       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2496       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2497       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2498       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2499       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2500     } else { // Smirf-like\r
2501       if(gameInfo.variant == VariantSChess) {\r
2502         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2503         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2504         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2505       } else {\r
2506         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2507         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2508         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2509       }\r
2510     }\r
2511     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2512       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2513       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2514       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2515     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2516       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2517       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2518       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2519     } else { // WinBoard standard\r
2520       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2521       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2522       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2523     }\r
2524   }\r
2525 \r
2526 \r
2527   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2528     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2529     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2530     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2531     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2532     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2533     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2534     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2535     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2536     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2537     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2538     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2539     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2540     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2541     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2542     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2543     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2544     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2545     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2546     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2547     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2548     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2549     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2550     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2551     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2552     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2553     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2554     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2555     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2556     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2557     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2558 \r
2559     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2560       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2561       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2562       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2563       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2564       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2565       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2566       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2567       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2568       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2569       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2570       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2571       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2572     } else {\r
2573       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2574       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2575       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2576       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2577       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2578       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2579       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2580       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2581       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2582       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2583       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2584       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2585     }\r
2586 \r
2587   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2588     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2589     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2590     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2591     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2592     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2593     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2594     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2595     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2596     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2597     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2598     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2599     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2600     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2601     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2602   }\r
2603 \r
2604 \r
2605   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2606   /* special Shogi support in this size */\r
2607   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2608       for (piece = WhitePawn;\r
2609            (int) piece < (int) BlackPawn;\r
2610            piece = (ChessSquare) ((int) piece + 1)) {\r
2611         if (pieceBitmap[i][piece] != NULL)\r
2612           DeleteObject(pieceBitmap[i][piece]);\r
2613       }\r
2614     }\r
2615   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2616   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2617   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2618   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2619   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2620   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2621   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2622   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2623   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2624   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2625   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2626   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2627   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2628   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2629   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2630   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2631   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2632   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2633   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2634   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2635   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2636   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2637   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2638   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2639   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2640   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2641   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2642   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2643   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2644   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2645   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2646   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2647   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2648   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2649   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2650   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2651   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2652   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2653   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2654   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2655   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2656   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2657   minorSize = 0;\r
2658   }\r
2659 }\r
2660 \r
2661 HBITMAP\r
2662 PieceBitmap(ChessSquare p, int kind)\r
2663 {\r
2664   if ((int) p >= (int) BlackPawn)\r
2665     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2666 \r
2667   return pieceBitmap[kind][(int) p];\r
2668 }\r
2669 \r
2670 /***************************************************************/\r
2671 \r
2672 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2673 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2674 /*\r
2675 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2676 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2677 */\r
2678 \r
2679 VOID\r
2680 SquareToPos(int row, int column, int * x, int * y)\r
2681 {\r
2682   if (flipView) {\r
2683     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2684     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2685   } else {\r
2686     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2687     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2688   }\r
2689 }\r
2690 \r
2691 VOID\r
2692 DrawCoordsOnDC(HDC hdc)\r
2693 {\r
2694   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
2695   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
2696   char str[2] = { NULLCHAR, NULLCHAR };\r
2697   int oldMode, oldAlign, x, y, start, i;\r
2698   HFONT oldFont;\r
2699   HBRUSH oldBrush;\r
2700 \r
2701   if (!appData.showCoords)\r
2702     return;\r
2703 \r
2704   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2705 \r
2706   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2707   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2708   oldAlign = GetTextAlign(hdc);\r
2709   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2710 \r
2711   y = boardRect.top + lineGap;\r
2712   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2713 \r
2714   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2715   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2716     str[0] = files[start + i];\r
2717     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2718     y += squareSize + lineGap;\r
2719   }\r
2720 \r
2721   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2722 \r
2723   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2724   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2725     str[0] = ranks[start + i];\r
2726     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2727     x += squareSize + lineGap;\r
2728   }    \r
2729 \r
2730   SelectObject(hdc, oldBrush);\r
2731   SetBkMode(hdc, oldMode);\r
2732   SetTextAlign(hdc, oldAlign);\r
2733   SelectObject(hdc, oldFont);\r
2734 }\r
2735 \r
2736 VOID\r
2737 DrawGridOnDC(HDC hdc)\r
2738 {\r
2739   HPEN oldPen;\r
2740  \r
2741   if (lineGap != 0) {\r
2742     oldPen = SelectObject(hdc, gridPen);\r
2743     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2744     SelectObject(hdc, oldPen);\r
2745   }\r
2746 }\r
2747 \r
2748 #define HIGHLIGHT_PEN 0\r
2749 #define PREMOVE_PEN   1\r
2750 \r
2751 VOID\r
2752 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2753 {\r
2754   int x1, y1;\r
2755   HPEN oldPen, hPen;\r
2756   if (lineGap == 0) return;\r
2757   if (flipView) {\r
2758     x1 = boardRect.left +\r
2759       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2760     y1 = boardRect.top +\r
2761       lineGap/2 + y * (squareSize + lineGap);\r
2762   } else {\r
2763     x1 = boardRect.left +\r
2764       lineGap/2 + x * (squareSize + lineGap);\r
2765     y1 = boardRect.top +\r
2766       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2767   }\r
2768   hPen = pen ? premovePen : highlightPen;\r
2769   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2770   MoveToEx(hdc, x1, y1, NULL);\r
2771   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2772   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2773   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2774   LineTo(hdc, x1, y1);\r
2775   SelectObject(hdc, oldPen);\r
2776 }\r
2777 \r
2778 VOID\r
2779 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2780 {\r
2781   int i;\r
2782   for (i=0; i<2; i++) {\r
2783     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2784       DrawHighlightOnDC(hdc, TRUE,\r
2785                         h->sq[i].x, h->sq[i].y,\r
2786                         pen);\r
2787   }\r
2788 }\r
2789 \r
2790 /* Note: sqcolor is used only in monoMode */\r
2791 /* Note that this code is largely duplicated in woptions.c,\r
2792    function DrawSampleSquare, so that needs to be updated too */\r
2793 VOID\r
2794 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2795 {\r
2796   HBITMAP oldBitmap;\r
2797   HBRUSH oldBrush;\r
2798   int tmpSize;\r
2799 \r
2800   if (appData.blindfold) return;\r
2801 \r
2802   /* [AS] Use font-based pieces if needed */\r
2803   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2804     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2805     CreatePiecesFromFont();\r
2806 \r
2807     if( fontBitmapSquareSize == squareSize ) {\r
2808         int index = TranslatePieceToFontPiece(piece);\r
2809 \r
2810         SelectObject( tmphdc, hPieceMask[ index ] );\r
2811 \r
2812       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2813         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2814       else\r
2815         BitBlt( hdc,\r
2816             x, y,\r
2817             squareSize, squareSize,\r
2818             tmphdc,\r
2819             0, 0,\r
2820             SRCAND );\r
2821 \r
2822         SelectObject( tmphdc, hPieceFace[ index ] );\r
2823 \r
2824       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2825         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2826       else\r
2827         BitBlt( hdc,\r
2828             x, y,\r
2829             squareSize, squareSize,\r
2830             tmphdc,\r
2831             0, 0,\r
2832             SRCPAINT );\r
2833 \r
2834         return;\r
2835     }\r
2836   }\r
2837 \r
2838   if (appData.monoMode) {\r
2839     SelectObject(tmphdc, PieceBitmap(piece, \r
2840       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2841     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2842            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2843   } else {\r
2844     tmpSize = squareSize;\r
2845     if(minorSize &&\r
2846         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2847          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2848       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2849       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2850       x += (squareSize - minorSize)>>1;\r
2851       y += squareSize - minorSize - 2;\r
2852       tmpSize = minorSize;\r
2853     }\r
2854     if (color || appData.allWhite ) {\r
2855       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2856       if( color )\r
2857               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2858       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2859       if(appData.upsideDown && color==flipView)\r
2860         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2861       else\r
2862         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2863       /* Use black for outline of white pieces */\r
2864       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2865       if(appData.upsideDown && color==flipView)\r
2866         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2867       else\r
2868         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2869     } else {\r
2870       /* Use square color for details of black pieces */\r
2871       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2872       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2873       if(appData.upsideDown && !flipView)\r
2874         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2875       else\r
2876         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2877     }\r
2878     SelectObject(hdc, oldBrush);\r
2879     SelectObject(tmphdc, oldBitmap);\r
2880   }\r
2881 }\r
2882 \r
2883 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2884 int GetBackTextureMode( int algo )\r
2885 {\r
2886     int result = BACK_TEXTURE_MODE_DISABLED;\r
2887 \r
2888     switch( algo ) \r
2889     {\r
2890         case BACK_TEXTURE_MODE_PLAIN:\r
2891             result = 1; /* Always use identity map */\r
2892             break;\r
2893         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2894             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2895             break;\r
2896     }\r
2897 \r
2898     return result;\r
2899 }\r
2900 \r
2901 /* \r
2902     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2903     to handle redraws cleanly (as random numbers would always be different).\r
2904 */\r
2905 VOID RebuildTextureSquareInfo()\r
2906 {\r
2907     BITMAP bi;\r
2908     int lite_w = 0;\r
2909     int lite_h = 0;\r
2910     int dark_w = 0;\r
2911     int dark_h = 0;\r
2912     int row;\r
2913     int col;\r
2914 \r
2915     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2916 \r
2917     if( liteBackTexture != NULL ) {\r
2918         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2919             lite_w = bi.bmWidth;\r
2920             lite_h = bi.bmHeight;\r
2921         }\r
2922     }\r
2923 \r
2924     if( darkBackTexture != NULL ) {\r
2925         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2926             dark_w = bi.bmWidth;\r
2927             dark_h = bi.bmHeight;\r
2928         }\r
2929     }\r
2930 \r
2931     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2932         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2933             if( (col + row) & 1 ) {\r
2934                 /* Lite square */\r
2935                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2936                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2937                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2938                   else\r
2939                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2940                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2941                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2942                   else\r
2943                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2944                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2945                 }\r
2946             }\r
2947             else {\r
2948                 /* Dark square */\r
2949                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2950                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2951                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2952                   else\r
2953                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2954                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2955                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2956                   else\r
2957                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2958                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2959                 }\r
2960             }\r
2961         }\r
2962     }\r
2963 }\r
2964 \r
2965 /* [AS] Arrow highlighting support */\r
2966 \r
2967 static double A_WIDTH = 5; /* Width of arrow body */\r
2968 \r
2969 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2970 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2971 \r
2972 static double Sqr( double x )\r
2973 {\r
2974     return x*x;\r
2975 }\r
2976 \r
2977 static int Round( double x )\r
2978 {\r
2979     return (int) (x + 0.5);\r
2980 }\r
2981 \r
2982 /* Draw an arrow between two points using current settings */\r
2983 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2984 {\r
2985     POINT arrow[7];\r
2986     double dx, dy, j, k, x, y;\r
2987 \r
2988     if( d_x == s_x ) {\r
2989         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2990 \r
2991         arrow[0].x = s_x + A_WIDTH + 0.5;\r
2992         arrow[0].y = s_y;\r
2993 \r
2994         arrow[1].x = s_x + A_WIDTH + 0.5;\r
2995         arrow[1].y = d_y - h;\r
2996 \r
2997         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2998         arrow[2].y = d_y - h;\r
2999 \r
3000         arrow[3].x = d_x;\r
3001         arrow[3].y = d_y;\r
3002 \r
3003         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3004         arrow[5].y = d_y - h;\r
3005 \r
3006         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3007         arrow[4].y = d_y - h;\r
3008 \r
3009         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3010         arrow[6].y = s_y;\r
3011     }\r
3012     else if( d_y == s_y ) {\r
3013         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3014 \r
3015         arrow[0].x = s_x;\r
3016         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3017 \r
3018         arrow[1].x = d_x - w;\r
3019         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3020 \r
3021         arrow[2].x = d_x - w;\r
3022         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3023 \r
3024         arrow[3].x = d_x;\r
3025         arrow[3].y = d_y;\r
3026 \r
3027         arrow[5].x = d_x - w;\r
3028         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3029 \r
3030         arrow[4].x = d_x - w;\r
3031         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3032 \r
3033         arrow[6].x = s_x;\r
3034         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3035     }\r
3036     else {\r
3037         /* [AS] Needed a lot of paper for this! :-) */\r
3038         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3039         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3040   \r
3041         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3042 \r
3043         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3044 \r
3045         x = s_x;\r
3046         y = s_y;\r
3047 \r
3048         arrow[0].x = Round(x - j);\r
3049         arrow[0].y = Round(y + j*dx);\r
3050 \r
3051         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3052         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3053 \r
3054         if( d_x > s_x ) {\r
3055             x = (double) d_x - k;\r
3056             y = (double) d_y - k*dy;\r
3057         }\r
3058         else {\r
3059             x = (double) d_x + k;\r
3060             y = (double) d_y + k*dy;\r
3061         }\r
3062 \r
3063         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3064 \r
3065         arrow[6].x = Round(x - j);\r
3066         arrow[6].y = Round(y + j*dx);\r
3067 \r
3068         arrow[2].x = Round(arrow[6].x + 2*j);\r
3069         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3070 \r
3071         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3072         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3073 \r
3074         arrow[4].x = d_x;\r
3075         arrow[4].y = d_y;\r
3076 \r
3077         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3078         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3079     }\r
3080 \r
3081     Polygon( hdc, arrow, 7 );\r
3082 }\r
3083 \r
3084 /* [AS] Draw an arrow between two squares */\r
3085 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3086 {\r
3087     int s_x, s_y, d_x, d_y;\r
3088     HPEN hpen;\r
3089     HPEN holdpen;\r
3090     HBRUSH hbrush;\r
3091     HBRUSH holdbrush;\r
3092     LOGBRUSH stLB;\r
3093 \r
3094     if( s_col == d_col && s_row == d_row ) {\r
3095         return;\r
3096     }\r
3097 \r
3098     /* Get source and destination points */\r
3099     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3100     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3101 \r
3102     if( d_y > s_y ) {\r
3103         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3104     }\r
3105     else if( d_y < s_y ) {\r
3106         d_y += squareSize / 2 + squareSize / 4;\r
3107     }\r
3108     else {\r
3109         d_y += squareSize / 2;\r
3110     }\r
3111 \r
3112     if( d_x > s_x ) {\r
3113         d_x += squareSize / 2 - squareSize / 4;\r
3114     }\r
3115     else if( d_x < s_x ) {\r
3116         d_x += squareSize / 2 + squareSize / 4;\r
3117     }\r
3118     else {\r
3119         d_x += squareSize / 2;\r
3120     }\r
3121 \r
3122     s_x += squareSize / 2;\r
3123     s_y += squareSize / 2;\r
3124 \r
3125     /* Adjust width */\r
3126     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3127 \r
3128     /* Draw */\r
3129     stLB.lbStyle = BS_SOLID;\r
3130     stLB.lbColor = appData.highlightArrowColor;\r
3131     stLB.lbHatch = 0;\r
3132 \r
3133     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3134     holdpen = SelectObject( hdc, hpen );\r
3135     hbrush = CreateBrushIndirect( &stLB );\r
3136     holdbrush = SelectObject( hdc, hbrush );\r
3137 \r
3138     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3139 \r
3140     SelectObject( hdc, holdpen );\r
3141     SelectObject( hdc, holdbrush );\r
3142     DeleteObject( hpen );\r
3143     DeleteObject( hbrush );\r
3144 }\r
3145 \r
3146 BOOL HasHighlightInfo()\r
3147 {\r
3148     BOOL result = FALSE;\r
3149 \r
3150     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3151         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3152     {\r
3153         result = TRUE;\r
3154     }\r
3155 \r
3156     return result;\r
3157 }\r
3158 \r
3159 BOOL IsDrawArrowEnabled()\r
3160 {\r
3161     BOOL result = FALSE;\r
3162 \r
3163     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3164         result = TRUE;\r
3165     }\r
3166 \r
3167     return result;\r
3168 }\r
3169 \r
3170 VOID DrawArrowHighlight( HDC hdc )\r
3171 {\r
3172     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3173         DrawArrowBetweenSquares( hdc,\r
3174             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3175             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3176     }\r
3177 }\r
3178 \r
3179 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3180 {\r
3181     HRGN result = NULL;\r
3182 \r
3183     if( HasHighlightInfo() ) {\r
3184         int x1, y1, x2, y2;\r
3185         int sx, sy, dx, dy;\r
3186 \r
3187         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3188         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3189 \r
3190         sx = MIN( x1, x2 );\r
3191         sy = MIN( y1, y2 );\r
3192         dx = MAX( x1, x2 ) + squareSize;\r
3193         dy = MAX( y1, y2 ) + squareSize;\r
3194 \r
3195         result = CreateRectRgn( sx, sy, dx, dy );\r
3196     }\r
3197 \r
3198     return result;\r
3199 }\r
3200 \r
3201 /*\r
3202     Warning: this function modifies the behavior of several other functions. \r
3203     \r
3204     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3205     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3206     repaint is scattered all over the place, which is not good for features such as\r
3207     "arrow highlighting" that require a full repaint of the board.\r
3208 \r
3209     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3210     user interaction, when speed is not so important) but especially to avoid errors\r
3211     in the displayed graphics.\r
3212 \r
3213     In such patched places, I always try refer to this function so there is a single\r
3214     place to maintain knowledge.\r
3215     \r
3216     To restore the original behavior, just return FALSE unconditionally.\r
3217 */\r
3218 BOOL IsFullRepaintPreferrable()\r
3219 {\r
3220     BOOL result = FALSE;\r
3221 \r
3222     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3223         /* Arrow may appear on the board */\r
3224         result = TRUE;\r
3225     }\r
3226 \r
3227     return result;\r
3228 }\r
3229 \r
3230 /* \r
3231     This function is called by DrawPosition to know whether a full repaint must\r
3232     be forced or not.\r
3233 \r
3234     Only DrawPosition may directly call this function, which makes use of \r
3235     some state information. Other function should call DrawPosition specifying \r
3236     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3237 */\r
3238 BOOL DrawPositionNeedsFullRepaint()\r
3239 {\r
3240     BOOL result = FALSE;\r
3241 \r
3242     /* \r
3243         Probably a slightly better policy would be to trigger a full repaint\r
3244         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3245         but animation is fast enough that it's difficult to notice.\r
3246     */\r
3247     if( animInfo.piece == EmptySquare ) {\r
3248         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3249             result = TRUE;\r
3250         }\r
3251     }\r
3252 \r
3253     return result;\r
3254 }\r
3255 \r
3256 VOID\r
3257 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3258 {\r
3259   int row, column, x, y, square_color, piece_color;\r
3260   ChessSquare piece;\r
3261   HBRUSH oldBrush;\r
3262   HDC texture_hdc = NULL;\r
3263 \r
3264   /* [AS] Initialize background textures if needed */\r
3265   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3266       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3267       if( backTextureSquareSize != squareSize \r
3268        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3269           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3270           backTextureSquareSize = squareSize;\r
3271           RebuildTextureSquareInfo();\r
3272       }\r
3273 \r
3274       texture_hdc = CreateCompatibleDC( hdc );\r
3275   }\r
3276 \r
3277   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3278     for (column = 0; column < BOARD_WIDTH; column++) {\r
3279   \r
3280       SquareToPos(row, column, &x, &y);\r
3281 \r
3282       piece = board[row][column];\r
3283 \r
3284       square_color = ((column + row) % 2) == 1;\r
3285       if( gameInfo.variant == VariantXiangqi ) {\r
3286           square_color = !InPalace(row, column);\r
3287           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3288           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3289       }\r
3290       piece_color = (int) piece < (int) BlackPawn;\r
3291 \r
3292 \r
3293       /* [HGM] holdings file: light square or black */\r
3294       if(column == BOARD_LEFT-2) {\r
3295             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3296                 square_color = 1;\r
3297             else {\r
3298                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3299                 continue;\r
3300             }\r
3301       } else\r
3302       if(column == BOARD_RGHT + 1 ) {\r
3303             if( row < gameInfo.holdingsSize )\r
3304                 square_color = 1;\r
3305             else {\r
3306                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3307                 continue;\r
3308             }\r
3309       }\r
3310       if(column == BOARD_LEFT-1 ) /* left align */\r
3311             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3312       else if( column == BOARD_RGHT) /* right align */\r
3313             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3314       else\r
3315       if (appData.monoMode) {\r
3316         if (piece == EmptySquare) {\r
3317           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3318                  square_color ? WHITENESS : BLACKNESS);\r
3319         } else {\r
3320           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3321         }\r
3322       } \r
3323       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3324           /* [AS] Draw the square using a texture bitmap */\r
3325           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3326           int r = row, c = column; // [HGM] do not flip board in flipView\r
3327           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3328 \r
3329           DrawTile( x, y, \r
3330               squareSize, squareSize, \r
3331               hdc, \r
3332               texture_hdc,\r
3333               backTextureSquareInfo[r][c].mode,\r
3334               backTextureSquareInfo[r][c].x,\r
3335               backTextureSquareInfo[r][c].y );\r
3336 \r
3337           SelectObject( texture_hdc, hbm );\r
3338 \r
3339           if (piece != EmptySquare) {\r
3340               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3341           }\r
3342       }\r
3343       else {\r
3344         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3345 \r
3346         oldBrush = SelectObject(hdc, brush );\r
3347         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3348         SelectObject(hdc, oldBrush);\r
3349         if (piece != EmptySquare)\r
3350           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3351       }\r
3352     }\r
3353   }\r
3354 \r
3355   if( texture_hdc != NULL ) {\r
3356     DeleteDC( texture_hdc );\r
3357   }\r
3358 }\r
3359 \r
3360 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3361 void fputDW(FILE *f, int x)\r
3362 {\r
3363         fputc(x     & 255, f);\r
3364         fputc(x>>8  & 255, f);\r
3365         fputc(x>>16 & 255, f);\r
3366         fputc(x>>24 & 255, f);\r
3367 }\r
3368 \r
3369 #define MAX_CLIPS 200   /* more than enough */\r
3370 \r
3371 VOID\r
3372 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3373 {\r
3374 //  HBITMAP bufferBitmap;\r
3375   BITMAP bi;\r
3376 //  RECT Rect;\r
3377   HDC tmphdc;\r
3378   HBITMAP hbm;\r
3379   int w = 100, h = 50;\r
3380 \r
3381   if(logo == NULL) return;\r
3382 //  GetClientRect(hwndMain, &Rect);\r
3383 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3384 //                                      Rect.bottom-Rect.top+1);\r
3385   tmphdc = CreateCompatibleDC(hdc);\r
3386   hbm = SelectObject(tmphdc, logo);\r
3387   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3388             w = bi.bmWidth;\r
3389             h = bi.bmHeight;\r
3390   }\r
3391   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3392                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3393   SelectObject(tmphdc, hbm);\r
3394   DeleteDC(tmphdc);\r
3395 }\r
3396 \r
3397 VOID\r
3398 DisplayLogos()\r
3399 {\r
3400   if(logoHeight) {\r
3401         HDC hdc = GetDC(hwndMain);\r
3402         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3403         if(appData.autoLogo) {\r
3404           \r
3405           switch(gameMode) { // pick logos based on game mode\r
3406             case IcsObserving:\r
3407                 whiteLogo = second.programLogo; // ICS logo\r
3408                 blackLogo = second.programLogo;\r
3409             default:\r
3410                 break;\r
3411             case IcsPlayingWhite:\r
3412                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3413                 blackLogo = second.programLogo; // ICS logo\r
3414                 break;\r
3415             case IcsPlayingBlack:\r
3416                 whiteLogo = second.programLogo; // ICS logo\r
3417                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3418                 break;\r
3419             case TwoMachinesPlay:\r
3420                 if(first.twoMachinesColor[0] == 'b') {\r
3421                     whiteLogo = second.programLogo;\r
3422                     blackLogo = first.programLogo;\r
3423                 }\r
3424                 break;\r
3425             case MachinePlaysWhite:\r
3426                 blackLogo = userLogo;\r
3427                 break;\r
3428             case MachinePlaysBlack:\r
3429                 whiteLogo = userLogo;\r
3430                 blackLogo = first.programLogo;\r
3431           }\r
3432         }\r
3433         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3434         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3435         ReleaseDC(hwndMain, hdc);\r
3436   }\r
3437 }\r
3438 \r
3439 static HDC hdcSeek;\r
3440 \r
3441 // [HGM] seekgraph\r
3442 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3443 {\r
3444     POINT stPt;\r
3445     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3446     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3447     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3448     SelectObject( hdcSeek, hp );\r
3449 }\r
3450 \r
3451 // front-end wrapper for drawing functions to do rectangles\r
3452 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3453 {\r
3454     HPEN hp;\r
3455     RECT rc;\r
3456 \r
3457     if (hdcSeek == NULL) {\r
3458     hdcSeek = GetDC(hwndMain);\r
3459       if (!appData.monoMode) {\r
3460         SelectPalette(hdcSeek, hPal, FALSE);\r
3461         RealizePalette(hdcSeek);\r
3462       }\r
3463     }\r
3464     hp = SelectObject( hdcSeek, gridPen );\r
3465     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3466     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3467     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3468     SelectObject( hdcSeek, hp );\r
3469 }\r
3470 \r
3471 // front-end wrapper for putting text in graph\r
3472 void DrawSeekText(char *buf, int x, int y)\r
3473 {\r
3474         SIZE stSize;\r
3475         SetBkMode( hdcSeek, TRANSPARENT );\r
3476         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3477         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3478 }\r
3479 \r
3480 void DrawSeekDot(int x, int y, int color)\r
3481 {\r
3482         int square = color & 0x80;\r
3483         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3484                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3485         color &= 0x7F;\r
3486         if(square)\r
3487             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3488                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3489         else\r
3490             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3491                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3492             SelectObject(hdcSeek, oldBrush);\r
3493 }\r
3494 \r
3495 VOID\r
3496 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3497 {\r
3498   static Board lastReq[2], lastDrawn[2];\r
3499   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3500   static int lastDrawnFlipView = 0;\r
3501   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3502   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3503   HDC tmphdc;\r
3504   HDC hdcmem;\r
3505   HBITMAP bufferBitmap;\r
3506   HBITMAP oldBitmap;\r
3507   RECT Rect;\r
3508   HRGN clips[MAX_CLIPS];\r
3509   ChessSquare dragged_piece = EmptySquare;\r
3510   int nr = twoBoards*partnerUp;\r
3511 \r
3512   /* I'm undecided on this - this function figures out whether a full\r
3513    * repaint is necessary on its own, so there's no real reason to have the\r
3514    * caller tell it that.  I think this can safely be set to FALSE - but\r
3515    * if we trust the callers not to request full repaints unnessesarily, then\r
3516    * we could skip some clipping work.  In other words, only request a full\r
3517    * redraw when the majority of pieces have changed positions (ie. flip, \r
3518    * gamestart and similar)  --Hawk\r
3519    */\r
3520   Boolean fullrepaint = repaint;\r
3521 \r
3522   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3523 \r
3524   if( DrawPositionNeedsFullRepaint() ) {\r
3525       fullrepaint = TRUE;\r
3526   }\r
3527 \r
3528   if (board == NULL) {\r
3529     if (!lastReqValid[nr]) {\r
3530       return;\r
3531     }\r
3532     board = lastReq[nr];\r
3533   } else {\r
3534     CopyBoard(lastReq[nr], board);\r
3535     lastReqValid[nr] = 1;\r
3536   }\r
3537 \r
3538   if (doingSizing) {\r
3539     return;\r
3540   }\r
3541 \r
3542   if (IsIconic(hwndMain)) {\r
3543     return;\r
3544   }\r
3545 \r
3546   if (hdc == NULL) {\r
3547     hdc = GetDC(hwndMain);\r
3548     if (!appData.monoMode) {\r
3549       SelectPalette(hdc, hPal, FALSE);\r
3550       RealizePalette(hdc);\r
3551     }\r
3552     releaseDC = TRUE;\r
3553   } else {\r
3554     releaseDC = FALSE;\r
3555   }\r
3556 \r
3557   /* Create some work-DCs */\r
3558   hdcmem = CreateCompatibleDC(hdc);\r
3559   tmphdc = CreateCompatibleDC(hdc);\r
3560 \r
3561   /* If dragging is in progress, we temporarely remove the piece */\r
3562   /* [HGM] or temporarily decrease count if stacked              */\r
3563   /*       !! Moved to before board compare !!                   */\r
3564   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3565     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3566     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3567             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3568         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3569     } else \r
3570     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3571             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3572         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3573     } else \r
3574         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3575   }\r
3576 \r
3577   /* Figure out which squares need updating by comparing the \r
3578    * newest board with the last drawn board and checking if\r
3579    * flipping has changed.\r
3580    */\r
3581   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3582     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3583       for (column = 0; column < BOARD_WIDTH; column++) {\r
3584         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3585           SquareToPos(row, column, &x, &y);\r
3586           clips[num_clips++] =\r
3587             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3588         }\r
3589       }\r
3590     }\r
3591    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3592     for (i=0; i<2; i++) {\r
3593       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3594           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3595         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3596             lastDrawnHighlight.sq[i].y >= 0) {\r
3597           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3598                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3599           clips[num_clips++] =\r
3600             CreateRectRgn(x - lineGap, y - lineGap, \r
3601                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3602         }\r
3603         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3604           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3605           clips[num_clips++] =\r
3606             CreateRectRgn(x - lineGap, y - lineGap, \r
3607                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3608         }\r
3609       }\r
3610     }\r
3611     for (i=0; i<2; i++) {\r
3612       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3613           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3614         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3615             lastDrawnPremove.sq[i].y >= 0) {\r
3616           SquareToPos(lastDrawnPremove.sq[i].y,\r
3617                       lastDrawnPremove.sq[i].x, &x, &y);\r
3618           clips[num_clips++] =\r
3619             CreateRectRgn(x - lineGap, y - lineGap, \r
3620                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3621         }\r
3622         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3623             premoveHighlightInfo.sq[i].y >= 0) {\r
3624           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3625                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3626           clips[num_clips++] =\r
3627             CreateRectRgn(x - lineGap, y - lineGap, \r
3628                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3629         }\r
3630       }\r
3631     }\r
3632    } else { // nr == 1\r
3633         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3634         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3635         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3636         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3637       for (i=0; i<2; i++) {\r
3638         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3639             partnerHighlightInfo.sq[i].y >= 0) {\r
3640           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3641                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3642           clips[num_clips++] =\r
3643             CreateRectRgn(x - lineGap, y - lineGap, \r
3644                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3645         }\r
3646         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3647             oldPartnerHighlight.sq[i].y >= 0) {\r
3648           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3649                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3650           clips[num_clips++] =\r
3651             CreateRectRgn(x - lineGap, y - lineGap, \r
3652                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3653         }\r
3654       }\r
3655    }\r
3656   } else {\r
3657     fullrepaint = TRUE;\r
3658   }\r
3659 \r
3660   /* Create a buffer bitmap - this is the actual bitmap\r
3661    * being written to.  When all the work is done, we can\r
3662    * copy it to the real DC (the screen).  This avoids\r
3663    * the problems with flickering.\r
3664    */\r
3665   GetClientRect(hwndMain, &Rect);\r
3666   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3667                                         Rect.bottom-Rect.top+1);\r
3668   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3669   if (!appData.monoMode) {\r
3670     SelectPalette(hdcmem, hPal, FALSE);\r
3671   }\r
3672 \r
3673   /* Create clips for dragging */\r
3674   if (!fullrepaint) {\r
3675     if (dragInfo.from.x >= 0) {\r
3676       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3677       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3678     }\r
3679     if (dragInfo.start.x >= 0) {\r
3680       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3681       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3682     }\r
3683     if (dragInfo.pos.x >= 0) {\r
3684       x = dragInfo.pos.x - squareSize / 2;\r
3685       y = dragInfo.pos.y - squareSize / 2;\r
3686       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3687     }\r
3688     if (dragInfo.lastpos.x >= 0) {\r
3689       x = dragInfo.lastpos.x - squareSize / 2;\r
3690       y = dragInfo.lastpos.y - squareSize / 2;\r
3691       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3692     }\r
3693   }\r
3694 \r
3695   /* Are we animating a move?  \r
3696    * If so, \r
3697    *   - remove the piece from the board (temporarely)\r
3698    *   - calculate the clipping region\r
3699    */\r
3700   if (!fullrepaint) {\r
3701     if (animInfo.piece != EmptySquare) {\r
3702       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3703       x = boardRect.left + animInfo.lastpos.x;\r
3704       y = boardRect.top + animInfo.lastpos.y;\r
3705       x2 = boardRect.left + animInfo.pos.x;\r
3706       y2 = boardRect.top + animInfo.pos.y;\r
3707       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3708       /* Slight kludge.  The real problem is that after AnimateMove is\r
3709          done, the position on the screen does not match lastDrawn.\r
3710          This currently causes trouble only on e.p. captures in\r
3711          atomic, where the piece moves to an empty square and then\r
3712          explodes.  The old and new positions both had an empty square\r
3713          at the destination, but animation has drawn a piece there and\r
3714          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3715       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3716     }\r
3717   }\r
3718 \r
3719   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3720   if (num_clips == 0)\r
3721     fullrepaint = TRUE;\r
3722 \r
3723   /* Set clipping on the memory DC */\r
3724   if (!fullrepaint) {\r
3725     SelectClipRgn(hdcmem, clips[0]);\r
3726     for (x = 1; x < num_clips; x++) {\r
3727       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3728         abort();  // this should never ever happen!\r
3729     }\r
3730   }\r
3731 \r
3732   /* Do all the drawing to the memory DC */\r
3733   if(explodeInfo.radius) { // [HGM] atomic\r
3734         HBRUSH oldBrush;\r
3735         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3736         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3737         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3738         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3739         x += squareSize/2;\r
3740         y += squareSize/2;\r
3741         if(!fullrepaint) {\r
3742           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3743           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3744         }\r
3745         DrawGridOnDC(hdcmem);\r
3746         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3747         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3748         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3749         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3750         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3751         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3752         SelectObject(hdcmem, oldBrush);\r
3753   } else {\r
3754     DrawGridOnDC(hdcmem);\r
3755     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3756         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3757         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3758     } else {\r
3759         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3760         oldPartnerHighlight = partnerHighlightInfo;\r
3761     }\r
3762     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3763   }\r
3764   if(nr == 0) // [HGM] dual: markers only on left board\r
3765   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3766     for (column = 0; column < BOARD_WIDTH; column++) {\r
3767         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3768             HBRUSH oldBrush = SelectObject(hdcmem, \r
3769                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3770             SquareToPos(row, column, &x, &y);\r
3771             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3772                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3773             SelectObject(hdcmem, oldBrush);\r
3774         }\r
3775     }\r
3776   }\r
3777 \r
3778   if( appData.highlightMoveWithArrow ) {\r
3779     DrawArrowHighlight(hdcmem);\r
3780   }\r
3781 \r
3782   DrawCoordsOnDC(hdcmem);\r
3783 \r
3784   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3785                  /* to make sure lastDrawn contains what is actually drawn */\r
3786 \r
3787   /* Put the dragged piece back into place and draw it (out of place!) */\r
3788     if (dragged_piece != EmptySquare) {\r
3789     /* [HGM] or restack */\r
3790     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3791                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3792     else\r
3793     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3794                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3795     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3796     x = dragInfo.pos.x - squareSize / 2;\r
3797     y = dragInfo.pos.y - squareSize / 2;\r
3798     DrawPieceOnDC(hdcmem, dragged_piece,\r
3799                   ((int) dragged_piece < (int) BlackPawn), \r
3800                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3801   }   \r
3802   \r
3803   /* Put the animated piece back into place and draw it */\r
3804   if (animInfo.piece != EmptySquare) {\r
3805     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3806     x = boardRect.left + animInfo.pos.x;\r
3807     y = boardRect.top + animInfo.pos.y;\r
3808     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3809                   ((int) animInfo.piece < (int) BlackPawn),\r
3810                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3811   }\r
3812 \r
3813   /* Release the bufferBitmap by selecting in the old bitmap \r
3814    * and delete the memory DC\r
3815    */\r
3816   SelectObject(hdcmem, oldBitmap);\r
3817   DeleteDC(hdcmem);\r
3818 \r
3819   /* Set clipping on the target DC */\r
3820   if (!fullrepaint) {\r
3821     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3822         RECT rect;\r
3823         GetRgnBox(clips[x], &rect);\r
3824         DeleteObject(clips[x]);\r
3825         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3826                           rect.right + wpMain.width/2, rect.bottom);\r
3827     }\r
3828     SelectClipRgn(hdc, clips[0]);\r
3829     for (x = 1; x < num_clips; x++) {\r
3830       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3831         abort();   // this should never ever happen!\r
3832     } \r
3833   }\r
3834 \r
3835   /* Copy the new bitmap onto the screen in one go.\r
3836    * This way we avoid any flickering\r
3837    */\r
3838   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3839   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3840          boardRect.right - boardRect.left,\r
3841          boardRect.bottom - boardRect.top,\r
3842          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3843   if(saveDiagFlag) { \r
3844     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3845     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3846 \r
3847     GetObject(bufferBitmap, sizeof(b), &b);\r
3848     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3849         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3850         bih.biWidth = b.bmWidth;\r
3851         bih.biHeight = b.bmHeight;\r
3852         bih.biPlanes = 1;\r
3853         bih.biBitCount = b.bmBitsPixel;\r
3854         bih.biCompression = 0;\r
3855         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3856         bih.biXPelsPerMeter = 0;\r
3857         bih.biYPelsPerMeter = 0;\r
3858         bih.biClrUsed = 0;\r
3859         bih.biClrImportant = 0;\r
3860 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3861 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3862         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3863 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3864 \r
3865         wb = b.bmWidthBytes;\r
3866         // count colors\r
3867         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3868                 int k = ((int*) pData)[i];\r
3869                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3870                 if(j >= 16) break;\r
3871                 color[j] = k;\r
3872                 if(j >= nrColors) nrColors = j+1;\r
3873         }\r
3874         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3875                 INT p = 0;\r
3876                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3877                     for(w=0; w<(wb>>2); w+=2) {\r
3878                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3879                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3880                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3881                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3882                         pData[p++] = m | j<<4;\r
3883                     }\r
3884                     while(p&3) pData[p++] = 0;\r
3885                 }\r
3886                 fac = 3;\r
3887                 wb = ((wb+31)>>5)<<2;\r
3888         }\r
3889         // write BITMAPFILEHEADER\r
3890         fprintf(diagFile, "BM");\r
3891         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3892         fputDW(diagFile, 0);\r
3893         fputDW(diagFile, 0x36 + (fac?64:0));\r
3894         // write BITMAPINFOHEADER\r
3895         fputDW(diagFile, 40);\r
3896         fputDW(diagFile, b.bmWidth);\r
3897         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3898         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3899         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3900         fputDW(diagFile, 0);\r
3901         fputDW(diagFile, 0);\r
3902         fputDW(diagFile, 0);\r
3903         fputDW(diagFile, 0);\r
3904         fputDW(diagFile, 0);\r
3905         fputDW(diagFile, 0);\r
3906         // write color table\r
3907         if(fac)\r
3908         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3909         // write bitmap data\r
3910         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3911                 fputc(pData[i], diagFile);\r
3912      }\r
3913   }\r
3914 \r
3915   SelectObject(tmphdc, oldBitmap);\r
3916 \r
3917   /* Massive cleanup */\r
3918   for (x = 0; x < num_clips; x++)\r
3919     DeleteObject(clips[x]);\r
3920 \r
3921   DeleteDC(tmphdc);\r
3922   DeleteObject(bufferBitmap);\r
3923 \r
3924   if (releaseDC) \r
3925     ReleaseDC(hwndMain, hdc);\r
3926   \r
3927   if (lastDrawnFlipView != flipView && nr == 0) {\r
3928     if (flipView)\r
3929       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3930     else\r
3931       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3932   }\r
3933 \r
3934 /*  CopyBoard(lastDrawn, board);*/\r
3935   lastDrawnHighlight = highlightInfo;\r
3936   lastDrawnPremove   = premoveHighlightInfo;\r
3937   lastDrawnFlipView = flipView;\r
3938   lastDrawnValid[nr] = 1;\r
3939 }\r
3940 \r
3941 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3942 int\r
3943 SaveDiagram(f)\r
3944      FILE *f;\r
3945 {\r
3946     saveDiagFlag = 1; diagFile = f;\r
3947     HDCDrawPosition(NULL, TRUE, NULL);\r
3948 \r
3949     saveDiagFlag = 0;\r
3950 \r
3951 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3952     \r
3953     fclose(f);\r
3954     return TRUE;\r
3955 }\r
3956 \r
3957 \r
3958 /*---------------------------------------------------------------------------*\\r
3959 | CLIENT PAINT PROCEDURE\r
3960 |   This is the main event-handler for the WM_PAINT message.\r
3961 |\r
3962 \*---------------------------------------------------------------------------*/\r
3963 VOID\r
3964 PaintProc(HWND hwnd)\r
3965 {\r
3966   HDC         hdc;\r
3967   PAINTSTRUCT ps;\r
3968   HFONT       oldFont;\r
3969 \r
3970   if((hdc = BeginPaint(hwnd, &ps))) {\r
3971     if (IsIconic(hwnd)) {\r
3972       DrawIcon(hdc, 2, 2, iconCurrent);\r
3973     } else {\r
3974       if (!appData.monoMode) {\r
3975         SelectPalette(hdc, hPal, FALSE);\r
3976         RealizePalette(hdc);\r
3977       }\r
3978       HDCDrawPosition(hdc, 1, NULL);\r
3979       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3980         flipView = !flipView; partnerUp = !partnerUp;\r
3981         HDCDrawPosition(hdc, 1, NULL);\r
3982         flipView = !flipView; partnerUp = !partnerUp;\r
3983       }\r
3984       oldFont =\r
3985         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3986       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3987                  ETO_CLIPPED|ETO_OPAQUE,\r
3988                  &messageRect, messageText, strlen(messageText), NULL);\r
3989       SelectObject(hdc, oldFont);\r
3990       DisplayBothClocks();\r
3991       DisplayLogos();\r
3992     }\r
3993     EndPaint(hwnd,&ps);\r
3994   }\r
3995 \r
3996   return;\r
3997 }\r
3998 \r
3999 \r
4000 /*\r
4001  * If the user selects on a border boundary, return -1; if off the board,\r
4002  *   return -2.  Otherwise map the event coordinate to the square.\r
4003  * The offset boardRect.left or boardRect.top must already have been\r
4004  *   subtracted from x.\r
4005  */\r
4006 int EventToSquare(x, limit)\r
4007      int x, limit;\r
4008 {\r
4009   if (x <= 0)\r
4010     return -2;\r
4011   if (x < lineGap)\r
4012     return -1;\r
4013   x -= lineGap;\r
4014   if ((x % (squareSize + lineGap)) >= squareSize)\r
4015     return -1;\r
4016   x /= (squareSize + lineGap);\r
4017     if (x >= limit)\r
4018     return -2;\r
4019   return x;\r
4020 }\r
4021 \r
4022 typedef struct {\r
4023   char piece;\r
4024   int command;\r
4025   char* name;\r
4026 } DropEnable;\r
4027 \r
4028 DropEnable dropEnables[] = {\r
4029   { 'P', DP_Pawn, N_("Pawn") },\r
4030   { 'N', DP_Knight, N_("Knight") },\r
4031   { 'B', DP_Bishop, N_("Bishop") },\r
4032   { 'R', DP_Rook, N_("Rook") },\r
4033   { 'Q', DP_Queen, N_("Queen") },\r
4034 };\r
4035 \r
4036 VOID\r
4037 SetupDropMenu(HMENU hmenu)\r
4038 {\r
4039   int i, count, enable;\r
4040   char *p;\r
4041   extern char white_holding[], black_holding[];\r
4042   char item[MSG_SIZ];\r
4043 \r
4044   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4045     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4046                dropEnables[i].piece);\r
4047     count = 0;\r
4048     while (p && *p++ == dropEnables[i].piece) count++;\r
4049       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4050     enable = count > 0 || !appData.testLegality\r
4051       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4052                       && !appData.icsActive);\r
4053     ModifyMenu(hmenu, dropEnables[i].command,\r
4054                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4055                dropEnables[i].command, item);\r
4056   }\r
4057 }\r
4058 \r
4059 void DragPieceBegin(int x, int y)\r
4060 {\r
4061       dragInfo.lastpos.x = boardRect.left + x;\r
4062       dragInfo.lastpos.y = boardRect.top + y;\r
4063       dragInfo.from.x = fromX;\r
4064       dragInfo.from.y = fromY;\r
4065       dragInfo.start = dragInfo.from;\r
4066       SetCapture(hwndMain);\r
4067 }\r
4068 \r
4069 void DragPieceEnd(int x, int y)\r
4070 {\r
4071     ReleaseCapture();\r
4072     dragInfo.start.x = dragInfo.start.y = -1;\r
4073     dragInfo.from = dragInfo.start;\r
4074     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4075 }\r
4076 \r
4077 /* Event handler for mouse messages */\r
4078 VOID\r
4079 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4080 {\r
4081   int x, y, menuNr;\r
4082   POINT pt;\r
4083   static int recursive = 0;\r
4084   HMENU hmenu;\r
4085   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4086 \r
4087   if (recursive) {\r
4088     if (message == WM_MBUTTONUP) {\r
4089       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4090          to the middle button: we simulate pressing the left button too!\r
4091          */\r
4092       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4093       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4094     }\r
4095     return;\r
4096   }\r
4097   recursive++;\r
4098   \r
4099   pt.x = LOWORD(lParam);\r
4100   pt.y = HIWORD(lParam);\r
4101   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4102   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4103   if (!flipView && y >= 0) {\r
4104     y = BOARD_HEIGHT - 1 - y;\r
4105   }\r
4106   if (flipView && x >= 0) {\r
4107     x = BOARD_WIDTH - 1 - x;\r
4108   }\r
4109 \r
4110   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4111 \r
4112   switch (message) {\r
4113   case WM_LBUTTONDOWN:\r
4114       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4115         if (gameMode == EditPosition) {\r
4116           SetWhiteToPlayEvent();\r
4117         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4118           AdjustClock(flipClock, -1);\r
4119         } else if (gameMode == IcsPlayingBlack ||\r
4120                    gameMode == MachinePlaysWhite) {\r
4121           CallFlagEvent();\r
4122         }\r
4123       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4124         if (gameMode == EditPosition) {\r
4125           SetBlackToPlayEvent();\r
4126         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4127           AdjustClock(!flipClock, -1);\r
4128         } else if (gameMode == IcsPlayingWhite ||\r
4129                    gameMode == MachinePlaysBlack) {\r
4130           CallFlagEvent();\r
4131         }\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     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4152     if ((appData.animateDragging || appData.highlightDragging)\r
4153         && (wParam & MK_LBUTTON)\r
4154         && dragInfo.from.x >= 0) \r
4155     {\r
4156       BOOL full_repaint = FALSE;\r
4157 \r
4158       if (appData.animateDragging) {\r
4159         dragInfo.pos = pt;\r
4160       }\r
4161       if (appData.highlightDragging) {\r
4162         SetHighlights(fromX, fromY, x, y);\r
4163         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4164             full_repaint = TRUE;\r
4165         }\r
4166       }\r
4167       \r
4168       DrawPosition( full_repaint, NULL);\r
4169       \r
4170       dragInfo.lastpos = dragInfo.pos;\r
4171     }\r
4172     break;\r
4173 \r
4174   case WM_MOUSEWHEEL: // [DM]\r
4175     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4176        /* Mouse Wheel is being rolled forward\r
4177         * Play moves forward\r
4178         */\r
4179        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4180                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4181        /* Mouse Wheel is being rolled backward\r
4182         * Play moves backward\r
4183         */\r
4184        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4185                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4186     }\r
4187     break;\r
4188 \r
4189   case WM_MBUTTONUP:\r
4190   case WM_RBUTTONUP:\r
4191     ReleaseCapture();\r
4192     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4193     break;\r
4194  \r
4195   case WM_MBUTTONDOWN:\r
4196   case WM_RBUTTONDOWN:\r
4197     ErrorPopDown();\r
4198     ReleaseCapture();\r
4199     fromX = fromY = -1;\r
4200     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4201     dragInfo.start.x = dragInfo.start.y = -1;\r
4202     dragInfo.from = dragInfo.start;\r
4203     dragInfo.lastpos = dragInfo.pos;\r
4204     if (appData.highlightDragging) {\r
4205       ClearHighlights();\r
4206     }\r
4207     if(y == -2) {\r
4208       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4209       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4210           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4211       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4212           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4213       }\r
4214       break;\r
4215     }\r
4216     DrawPosition(TRUE, NULL);\r
4217 \r
4218     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4219     switch (menuNr) {\r
4220     case 0:\r
4221       if (message == WM_MBUTTONDOWN) {\r
4222         buttonCount = 3;  /* even if system didn't think so */\r
4223         if (wParam & MK_SHIFT) \r
4224           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4225         else\r
4226           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4227       } else { /* message == WM_RBUTTONDOWN */\r
4228         /* Just have one menu, on the right button.  Windows users don't\r
4229            think to try the middle one, and sometimes other software steals\r
4230            it, or it doesn't really exist. */\r
4231         if(gameInfo.variant != VariantShogi)\r
4232             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4233         else\r
4234             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4235       }\r
4236       break;\r
4237     case 2:\r
4238       SetCapture(hwndMain);
4239       break;\r
4240     case 1:\r
4241       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4242       SetupDropMenu(hmenu);\r
4243       MenuPopup(hwnd, pt, hmenu, -1);\r
4244     default:\r
4245       break;\r
4246     }\r
4247     break;\r
4248   }\r
4249 \r
4250   recursive--;\r
4251 }\r
4252 \r
4253 /* Preprocess messages for buttons in main window */\r
4254 LRESULT CALLBACK\r
4255 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4256 {\r
4257   int id = GetWindowLong(hwnd, GWL_ID);\r
4258   int i, dir;\r
4259 \r
4260   for (i=0; i<N_BUTTONS; i++) {\r
4261     if (buttonDesc[i].id == id) break;\r
4262   }\r
4263   if (i == N_BUTTONS) return 0;\r
4264   switch (message) {\r
4265   case WM_KEYDOWN:\r
4266     switch (wParam) {\r
4267     case VK_LEFT:\r
4268     case VK_RIGHT:\r
4269       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4270       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4271       return TRUE;\r
4272     }\r
4273     break;\r
4274   case WM_CHAR:\r
4275     switch (wParam) {\r
4276     case '\r':\r
4277       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4278       return TRUE;\r
4279     default:\r
4280       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4281         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4282         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4283         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4284         SetFocus(h);\r
4285         SendMessage(h, WM_CHAR, wParam, lParam);\r
4286         return TRUE;\r
4287       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4288         PopUpMoveDialog((char)wParam);\r
4289       }\r
4290       break;\r
4291     }\r
4292     break;\r
4293   }\r
4294   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4295 }\r
4296 \r
4297 /* Process messages for Promotion dialog box */\r
4298 LRESULT CALLBACK\r
4299 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4300 {\r
4301   char promoChar;\r
4302 \r
4303   switch (message) {\r
4304   case WM_INITDIALOG: /* message: initialize dialog box */\r
4305     /* Center the dialog over the application window */\r
4306     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4307     Translate(hDlg, DLG_PromotionKing);\r
4308     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4309       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4310        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4311        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4312                SW_SHOW : SW_HIDE);\r
4313     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4314     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4315        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4316          PieceToChar(WhiteAngel) != '~') ||\r
4317         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4318          PieceToChar(BlackAngel) != '~')   ) ?\r
4319                SW_SHOW : SW_HIDE);\r
4320     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4321        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4322          PieceToChar(WhiteMarshall) != '~') ||\r
4323         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4324          PieceToChar(BlackMarshall) != '~')   ) ?\r
4325                SW_SHOW : SW_HIDE);\r
4326     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4327     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4328        gameInfo.variant != VariantShogi ?\r
4329                SW_SHOW : SW_HIDE);\r
4330     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4331        gameInfo.variant != VariantShogi ?\r
4332                SW_SHOW : SW_HIDE);\r
4333     if(gameInfo.variant == VariantShogi) {\r
4334         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4335         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4336         SetWindowText(hDlg, "Promote?");\r
4337     }\r
4338     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4339        gameInfo.variant == VariantSuper ?\r
4340                SW_SHOW : SW_HIDE);\r
4341     return TRUE;\r
4342 \r
4343   case WM_COMMAND: /* message: received a command */\r
4344     switch (LOWORD(wParam)) {\r
4345     case IDCANCEL:\r
4346       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4347       ClearHighlights();\r
4348       DrawPosition(FALSE, NULL);\r
4349       return TRUE;\r
4350     case PB_King:\r
4351       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4352       break;\r
4353     case PB_Queen:\r
4354       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4355       break;\r
4356     case PB_Rook:\r
4357       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4358       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4359       break;\r
4360     case PB_Bishop:\r
4361       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4362       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4363       break;\r
4364     case PB_Chancellor:\r
4365       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4366       break;\r
4367     case PB_Archbishop:\r
4368       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4369       break;\r
4370     case PB_Knight:\r
4371       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4372       break;\r
4373     default:\r
4374       return FALSE;\r
4375     }\r
4376     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4377     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4378     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4379     fromX = fromY = -1;\r
4380     if (!appData.highlightLastMove) {\r
4381       ClearHighlights();\r
4382       DrawPosition(FALSE, NULL);\r
4383     }\r
4384     return TRUE;\r
4385   }\r
4386   return FALSE;\r
4387 }\r
4388 \r
4389 /* Pop up promotion dialog */\r
4390 VOID\r
4391 PromotionPopup(HWND hwnd)\r
4392 {\r
4393   FARPROC lpProc;\r
4394 \r
4395   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4396   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4397     hwnd, (DLGPROC)lpProc);\r
4398   FreeProcInstance(lpProc);\r
4399 }\r
4400 \r
4401 void\r
4402 PromotionPopUp()\r
4403 {\r
4404   DrawPosition(TRUE, NULL);\r
4405   PromotionPopup(hwndMain);\r
4406 }\r
4407 \r
4408 /* Toggle ShowThinking */\r
4409 VOID\r
4410 ToggleShowThinking()\r
4411 {\r
4412   appData.showThinking = !appData.showThinking;\r
4413   ShowThinkingEvent();\r
4414 }\r
4415 \r
4416 VOID\r
4417 LoadGameDialog(HWND hwnd, char* title)\r
4418 {\r
4419   UINT number = 0;\r
4420   FILE *f;\r
4421   char fileTitle[MSG_SIZ];\r
4422   f = OpenFileDialog(hwnd, "rb", "",\r
4423                      appData.oldSaveStyle ? "gam" : "pgn",\r
4424                      GAME_FILT,\r
4425                      title, &number, fileTitle, NULL);\r
4426   if (f != NULL) {\r
4427     cmailMsgLoaded = FALSE;\r
4428     if (number == 0) {\r
4429       int error = GameListBuild(f);\r
4430       if (error) {\r
4431         DisplayError(_("Cannot build game list"), error);\r
4432       } else if (!ListEmpty(&gameList) &&\r
4433                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4434         GameListPopUp(f, fileTitle);\r
4435         return;\r
4436       }\r
4437       GameListDestroy();\r
4438       number = 1;\r
4439     }\r
4440     LoadGame(f, number, fileTitle, FALSE);\r
4441   }\r
4442 }\r
4443 \r
4444 int get_term_width()\r
4445 {\r
4446     HDC hdc;\r
4447     TEXTMETRIC tm;\r
4448     RECT rc;\r
4449     HFONT hfont, hold_font;\r
4450     LOGFONT lf;\r
4451     HWND hText;\r
4452 \r
4453     if (hwndConsole)\r
4454         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4455     else\r
4456         return 79;\r
4457 \r
4458     // get the text metrics\r
4459     hdc = GetDC(hText);\r
4460     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4461     if (consoleCF.dwEffects & CFE_BOLD)\r
4462         lf.lfWeight = FW_BOLD;\r
4463     if (consoleCF.dwEffects & CFE_ITALIC)\r
4464         lf.lfItalic = TRUE;\r
4465     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4466         lf.lfStrikeOut = TRUE;\r
4467     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4468         lf.lfUnderline = TRUE;\r
4469     hfont = CreateFontIndirect(&lf);\r
4470     hold_font = SelectObject(hdc, hfont);\r
4471     GetTextMetrics(hdc, &tm);\r
4472     SelectObject(hdc, hold_font);\r
4473     DeleteObject(hfont);\r
4474     ReleaseDC(hText, hdc);\r
4475 \r
4476     // get the rectangle\r
4477     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4478 \r
4479     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4480 }\r
4481 \r
4482 void UpdateICSWidth(HWND hText)\r
4483 {\r
4484     LONG old_width, new_width;\r
4485 \r
4486     new_width = get_term_width(hText, FALSE);\r
4487     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4488     if (new_width != old_width)\r
4489     {\r
4490         ics_update_width(new_width);\r
4491         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4492     }\r
4493 }\r
4494 \r
4495 VOID\r
4496 ChangedConsoleFont()\r
4497 {\r
4498   CHARFORMAT cfmt;\r
4499   CHARRANGE tmpsel, sel;\r
4500   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4501   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4502   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4503   PARAFORMAT paraf;\r
4504 \r
4505   cfmt.cbSize = sizeof(CHARFORMAT);\r
4506   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4507     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4508                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4509   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4510    * size.  This was undocumented in the version of MSVC++ that I had\r
4511    * when I wrote the code, but is apparently documented now.\r
4512    */\r
4513   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4514   cfmt.bCharSet = f->lf.lfCharSet;\r
4515   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4516   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4517   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4518   /* Why are the following seemingly needed too? */\r
4519   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4520   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4521   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4522   tmpsel.cpMin = 0;\r
4523   tmpsel.cpMax = -1; /*999999?*/\r
4524   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4525   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4526   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4527    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4528    */\r
4529   paraf.cbSize = sizeof(paraf);\r
4530   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4531   paraf.dxStartIndent = 0;\r
4532   paraf.dxOffset = WRAP_INDENT;\r
4533   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4534   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4535   UpdateICSWidth(hText);\r
4536 }\r
4537 \r
4538 /*---------------------------------------------------------------------------*\\r
4539  *\r
4540  * Window Proc for main window\r
4541  *\r
4542 \*---------------------------------------------------------------------------*/\r
4543 \r
4544 /* Process messages for main window, etc. */\r
4545 LRESULT CALLBACK\r
4546 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4547 {\r
4548   FARPROC lpProc;\r
4549   int wmId, wmEvent;\r
4550   char *defName;\r
4551   FILE *f;\r
4552   UINT number;\r
4553   char fileTitle[MSG_SIZ];\r
4554   char buf[MSG_SIZ];\r
4555   static SnapData sd;\r
4556 \r
4557   switch (message) {\r
4558 \r
4559   case WM_PAINT: /* message: repaint portion of window */\r
4560     PaintProc(hwnd);\r
4561     break;\r
4562 \r
4563   case WM_ERASEBKGND:\r
4564     if (IsIconic(hwnd)) {\r
4565       /* Cheat; change the message */\r
4566       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4567     } else {\r
4568       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4569     }\r
4570     break;\r
4571 \r
4572   case WM_LBUTTONDOWN:\r
4573   case WM_MBUTTONDOWN:\r
4574   case WM_RBUTTONDOWN:\r
4575   case WM_LBUTTONUP:\r
4576   case WM_MBUTTONUP:\r
4577   case WM_RBUTTONUP:\r
4578   case WM_MOUSEMOVE:\r
4579   case WM_MOUSEWHEEL:\r
4580     MouseEvent(hwnd, message, wParam, lParam);\r
4581     break;\r
4582 \r
4583   JAWS_KB_NAVIGATION\r
4584 \r
4585   case WM_CHAR:\r
4586     \r
4587     JAWS_ALT_INTERCEPT\r
4588 \r
4589     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4590         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4591         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4592         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4593         SetFocus(h);\r
4594         SendMessage(h, message, wParam, lParam);\r
4595     } else if(lParam != KF_REPEAT) {\r
4596         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4597                 PopUpMoveDialog((char)wParam);\r
4598         } else if((char)wParam == 003) CopyGameToClipboard();\r
4599          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4600     }\r
4601 \r
4602     break;\r
4603 \r
4604   case WM_PALETTECHANGED:\r
4605     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4606       int nnew;\r
4607       HDC hdc = GetDC(hwndMain);\r
4608       SelectPalette(hdc, hPal, TRUE);\r
4609       nnew = RealizePalette(hdc);\r
4610       if (nnew > 0) {\r
4611         paletteChanged = TRUE;\r
4612         InvalidateRect(hwnd, &boardRect, FALSE);\r
4613       }\r
4614       ReleaseDC(hwnd, hdc);\r
4615     }\r
4616     break;\r
4617 \r
4618   case WM_QUERYNEWPALETTE:\r
4619     if (!appData.monoMode /*&& paletteChanged*/) {\r
4620       int nnew;\r
4621       HDC hdc = GetDC(hwndMain);\r
4622       paletteChanged = FALSE;\r
4623       SelectPalette(hdc, hPal, FALSE);\r
4624       nnew = RealizePalette(hdc);\r
4625       if (nnew > 0) {\r
4626         InvalidateRect(hwnd, &boardRect, FALSE);\r
4627       }\r
4628       ReleaseDC(hwnd, hdc);\r
4629       return TRUE;\r
4630     }\r
4631     return FALSE;\r
4632 \r
4633   case WM_COMMAND: /* message: command from application menu */\r
4634     wmId    = LOWORD(wParam);\r
4635     wmEvent = HIWORD(wParam);\r
4636 \r
4637     switch (wmId) {\r
4638     case IDM_NewGame:\r
4639       ResetGameEvent();\r
4640       SAY("new game enter a move to play against the computer with white");\r
4641       break;\r
4642 \r
4643     case IDM_NewGameFRC:\r
4644       if( NewGameFRC() == 0 ) {\r
4645         ResetGameEvent();\r
4646       }\r
4647       break;\r
4648 \r
4649     case IDM_NewVariant:\r
4650       NewVariantPopup(hwnd);\r
4651       break;\r
4652 \r
4653     case IDM_LoadGame:\r
4654       LoadGameDialog(hwnd, _("Load Game from File"));\r
4655       break;\r
4656 \r
4657     case IDM_LoadNextGame:\r
4658       ReloadGame(1);\r
4659       break;\r
4660 \r
4661     case IDM_LoadPrevGame:\r
4662       ReloadGame(-1);\r
4663       break;\r
4664 \r
4665     case IDM_ReloadGame:\r
4666       ReloadGame(0);\r
4667       break;\r
4668 \r
4669     case IDM_LoadPosition:\r
4670       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4671         Reset(FALSE, TRUE);\r
4672       }\r
4673       number = 1;\r
4674       f = OpenFileDialog(hwnd, "rb", "",\r
4675                          appData.oldSaveStyle ? "pos" : "fen",\r
4676                          POSITION_FILT,\r
4677                          _("Load Position from File"), &number, fileTitle, NULL);\r
4678       if (f != NULL) {\r
4679         LoadPosition(f, number, fileTitle);\r
4680       }\r
4681       break;\r
4682 \r
4683     case IDM_LoadNextPosition:\r
4684       ReloadPosition(1);\r
4685       break;\r
4686 \r
4687     case IDM_LoadPrevPosition:\r
4688       ReloadPosition(-1);\r
4689       break;\r
4690 \r
4691     case IDM_ReloadPosition:\r
4692       ReloadPosition(0);\r
4693       break;\r
4694 \r
4695     case IDM_SaveGame:\r
4696       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4697       f = OpenFileDialog(hwnd, "a", defName,\r
4698                          appData.oldSaveStyle ? "gam" : "pgn",\r
4699                          GAME_FILT,\r
4700                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4701       if (f != NULL) {\r
4702         SaveGame(f, 0, "");\r
4703       }\r
4704       break;\r
4705 \r
4706     case IDM_SavePosition:\r
4707       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4708       f = OpenFileDialog(hwnd, "a", defName,\r
4709                          appData.oldSaveStyle ? "pos" : "fen",\r
4710                          POSITION_FILT,\r
4711                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4712       if (f != NULL) {\r
4713         SavePosition(f, 0, "");\r
4714       }\r
4715       break;\r
4716 \r
4717     case IDM_SaveDiagram:\r
4718       defName = "diagram";\r
4719       f = OpenFileDialog(hwnd, "wb", defName,\r
4720                          "bmp",\r
4721                          DIAGRAM_FILT,\r
4722                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4723       if (f != NULL) {\r
4724         SaveDiagram(f);\r
4725       }\r
4726       break;\r
4727 \r
4728     case IDM_CopyGame:\r
4729       CopyGameToClipboard();\r
4730       break;\r
4731 \r
4732     case IDM_PasteGame:\r
4733       PasteGameFromClipboard();\r
4734       break;\r
4735 \r
4736     case IDM_CopyGameListToClipboard:\r
4737       CopyGameListToClipboard();\r
4738       break;\r
4739 \r
4740     /* [AS] Autodetect FEN or PGN data */\r
4741     case IDM_PasteAny:\r
4742       PasteGameOrFENFromClipboard();\r
4743       break;\r
4744 \r
4745     /* [AS] Move history */\r
4746     case IDM_ShowMoveHistory:\r
4747         if( MoveHistoryIsUp() ) {\r
4748             MoveHistoryPopDown();\r
4749         }\r
4750         else {\r
4751             MoveHistoryPopUp();\r
4752         }\r
4753         break;\r
4754 \r
4755     /* [AS] Eval graph */\r
4756     case IDM_ShowEvalGraph:\r
4757         if( EvalGraphIsUp() ) {\r
4758             EvalGraphPopDown();\r
4759         }\r
4760         else {\r
4761             EvalGraphPopUp();\r
4762             SetFocus(hwndMain);\r
4763         }\r
4764         break;\r
4765 \r
4766     /* [AS] Engine output */\r
4767     case IDM_ShowEngineOutput:\r
4768         if( EngineOutputIsUp() ) {\r
4769             EngineOutputPopDown();\r
4770         }\r
4771         else {\r
4772             EngineOutputPopUp();\r
4773         }\r
4774         break;\r
4775 \r
4776     /* [AS] User adjudication */\r
4777     case IDM_UserAdjudication_White:\r
4778         UserAdjudicationEvent( +1 );\r
4779         break;\r
4780 \r
4781     case IDM_UserAdjudication_Black:\r
4782         UserAdjudicationEvent( -1 );\r
4783         break;\r
4784 \r
4785     case IDM_UserAdjudication_Draw:\r
4786         UserAdjudicationEvent( 0 );\r
4787         break;\r
4788 \r
4789     /* [AS] Game list options dialog */\r
4790     case IDM_GameListOptions:\r
4791       GameListOptions();\r
4792       break;\r
4793 \r
4794     case IDM_NewChat:\r
4795       ChatPopUp(NULL);\r
4796       break;\r
4797 \r
4798     case IDM_CopyPosition:\r
4799       CopyFENToClipboard();\r
4800       break;\r
4801 \r
4802     case IDM_PastePosition:\r
4803       PasteFENFromClipboard();\r
4804       break;\r
4805 \r
4806     case IDM_MailMove:\r
4807       MailMoveEvent();\r
4808       break;\r
4809 \r
4810     case IDM_ReloadCMailMsg:\r
4811       Reset(TRUE, TRUE);\r
4812       ReloadCmailMsgEvent(FALSE);\r
4813       break;\r
4814 \r
4815     case IDM_Minimize:\r
4816       ShowWindow(hwnd, SW_MINIMIZE);\r
4817       break;\r
4818 \r
4819     case IDM_Exit:\r
4820       ExitEvent(0);\r
4821       break;\r
4822 \r
4823     case IDM_MachineWhite:\r
4824       MachineWhiteEvent();\r
4825       /*\r
4826        * refresh the tags dialog only if it's visible\r
4827        */\r
4828       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4829           char *tags;\r
4830           tags = PGNTags(&gameInfo);\r
4831           TagsPopUp(tags, CmailMsg());\r
4832           free(tags);\r
4833       }\r
4834       SAY("computer starts playing white");\r
4835       break;\r
4836 \r
4837     case IDM_MachineBlack:\r
4838       MachineBlackEvent();\r
4839       /*\r
4840        * refresh the tags dialog only if it's visible\r
4841        */\r
4842       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4843           char *tags;\r
4844           tags = PGNTags(&gameInfo);\r
4845           TagsPopUp(tags, CmailMsg());\r
4846           free(tags);\r
4847       }\r
4848       SAY("computer starts playing black");\r
4849       break;\r
4850 \r
4851     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4852       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4853         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4854       }\r
4855       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4856       appData.matchGames = appData.defaultMatchGames;\r
4857       matchGame = 1;\r
4858       first.matchWins = second.matchWins = 0;\r
4859 \r
4860     case IDM_TwoMachines:\r
4861       TwoMachinesEvent();\r
4862       /*\r
4863        * refresh the tags dialog only if it's visible\r
4864        */\r
4865       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4866           char *tags;\r
4867           tags = PGNTags(&gameInfo);\r
4868           TagsPopUp(tags, CmailMsg());\r
4869           free(tags);\r
4870       }\r
4871       SAY("computer starts playing both sides");\r
4872       break;\r
4873 \r
4874     case IDM_AnalysisMode:\r
4875       if (!first.analysisSupport) {\r
4876         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4877         DisplayError(buf, 0);\r
4878       } else {\r
4879         SAY("analyzing current position");\r
4880         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4881         if (appData.icsActive) {\r
4882                if (gameMode != IcsObserving) {\r
4883                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4884                        DisplayError(buf, 0);\r
4885                        /* secure check */\r
4886                        if (appData.icsEngineAnalyze) {\r
4887                                if (appData.debugMode) \r
4888                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4889                                ExitAnalyzeMode();\r
4890                                ModeHighlight();\r
4891                                break;\r
4892                        }\r
4893                        break;\r
4894                } else {\r
4895                        /* if enable, user want disable icsEngineAnalyze */\r
4896                        if (appData.icsEngineAnalyze) {\r
4897                                ExitAnalyzeMode();\r
4898                                ModeHighlight();\r
4899                                break;\r
4900                        }\r
4901                        appData.icsEngineAnalyze = TRUE;\r
4902                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4903                }\r
4904         } \r
4905         if (!appData.showThinking) ToggleShowThinking();\r
4906         AnalyzeModeEvent();\r
4907       }\r
4908       break;\r
4909 \r
4910     case IDM_AnalyzeFile:\r
4911       if (!first.analysisSupport) {\r
4912         char buf[MSG_SIZ];\r
4913           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4914         DisplayError(buf, 0);\r
4915       } else {\r
4916         if (!appData.showThinking) ToggleShowThinking();\r
4917         AnalyzeFileEvent();\r
4918         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4919         AnalysisPeriodicEvent(1);\r
4920       }\r
4921       break;\r
4922 \r
4923     case IDM_IcsClient:\r
4924       IcsClientEvent();\r
4925       break;\r
4926 \r
4927     case IDM_EditGame:\r
4928     case IDM_EditGame2:\r
4929       EditGameEvent();\r
4930       SAY("edit game");\r
4931       break;\r
4932 \r
4933     case IDM_EditPosition:\r
4934     case IDM_EditPosition2:\r
4935       EditPositionEvent();\r
4936       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4937       break;\r
4938 \r
4939     case IDM_Training:\r
4940       TrainingEvent();\r
4941       break;\r
4942 \r
4943     case IDM_ShowGameList:\r
4944       ShowGameListProc();\r
4945       break;\r
4946 \r
4947     case IDM_EditProgs1:\r
4948       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4949       break;\r
4950 \r
4951     case IDM_EditProgs2:\r
4952       EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4953       break;\r
4954 \r
4955     case IDM_EditServers:\r
4956       EditTagsPopUp(icsNames, &icsNames);\r
4957       break;\r
4958 \r
4959     case IDM_EditTags:\r
4960     case IDM_Tags:\r
4961       EditTagsProc();\r
4962       break;\r
4963 \r
4964     case IDM_EditComment:\r
4965     case IDM_Comment:\r
4966       if (commentUp && editComment) {\r
4967         CommentPopDown();\r
4968       } else {\r
4969         EditCommentEvent();\r
4970       }\r
4971       break;\r
4972 \r
4973     case IDM_Pause:\r
4974       PauseEvent();\r
4975       break;\r
4976 \r
4977     case IDM_Accept:\r
4978       AcceptEvent();\r
4979       break;\r
4980 \r
4981     case IDM_Decline:\r
4982       DeclineEvent();\r
4983       break;\r
4984 \r
4985     case IDM_Rematch:\r
4986       RematchEvent();\r
4987       break;\r
4988 \r
4989     case IDM_CallFlag:\r
4990       CallFlagEvent();\r
4991       break;\r
4992 \r
4993     case IDM_Draw:\r
4994       DrawEvent();\r
4995       break;\r
4996 \r
4997     case IDM_Adjourn:\r
4998       AdjournEvent();\r
4999       break;\r
5000 \r
5001     case IDM_Abort:\r
5002       AbortEvent();\r
5003       break;\r
5004 \r
5005     case IDM_Resign:\r
5006       ResignEvent();\r
5007       break;\r
5008 \r
5009     case IDM_StopObserving:\r
5010       StopObservingEvent();\r
5011       break;\r
5012 \r
5013     case IDM_StopExamining:\r
5014       StopExaminingEvent();\r
5015       break;\r
5016 \r
5017     case IDM_Upload:\r
5018       UploadGameEvent();\r
5019       break;\r
5020 \r
5021     case IDM_TypeInMove:\r
5022       PopUpMoveDialog('\000');\r
5023       break;\r
5024 \r
5025     case IDM_TypeInName:\r
5026       PopUpNameDialog('\000');\r
5027       break;\r
5028 \r
5029     case IDM_Backward:\r
5030       BackwardEvent();\r
5031       SetFocus(hwndMain);\r
5032       break;\r
5033 \r
5034     JAWS_MENU_ITEMS\r
5035 \r
5036     case IDM_Forward:\r
5037       ForwardEvent();\r
5038       SetFocus(hwndMain);\r
5039       break;\r
5040 \r
5041     case IDM_ToStart:\r
5042       ToStartEvent();\r
5043       SetFocus(hwndMain);\r
5044       break;\r
5045 \r
5046     case IDM_ToEnd:\r
5047       ToEndEvent();\r
5048       SetFocus(hwndMain);\r
5049       break;\r
5050 \r
5051     case IDM_Revert:\r
5052       RevertEvent(FALSE);\r
5053       break;\r
5054 \r
5055     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5056       RevertEvent(TRUE);\r
5057       break;\r
5058 \r
5059     case IDM_TruncateGame:\r
5060       TruncateGameEvent();\r
5061       break;\r
5062 \r
5063     case IDM_MoveNow:\r
5064       MoveNowEvent();\r
5065       break;\r
5066 \r
5067     case IDM_RetractMove:\r
5068       RetractMoveEvent();\r
5069       break;\r
5070 \r
5071     case IDM_FlipView:\r
5072       flipView = !flipView;\r
5073       DrawPosition(FALSE, NULL);\r
5074       break;\r
5075 \r
5076     case IDM_FlipClock:\r
5077       flipClock = !flipClock;\r
5078       DisplayBothClocks();\r
5079       DisplayLogos();\r
5080       break;\r
5081 \r
5082     case IDM_MuteSounds:\r
5083       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5084       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5085                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5086       break;\r
5087 \r
5088     case IDM_GeneralOptions:\r
5089       GeneralOptionsPopup(hwnd);\r
5090       DrawPosition(TRUE, NULL);\r
5091       break;\r
5092 \r
5093     case IDM_BoardOptions:\r
5094       BoardOptionsPopup(hwnd);\r
5095       break;\r
5096 \r
5097     case IDM_EnginePlayOptions:\r
5098       EnginePlayOptionsPopup(hwnd);\r
5099       break;\r
5100 \r
5101     case IDM_Engine1Options:\r
5102       EngineOptionsPopup(hwnd, &first);\r
5103       break;\r
5104 \r
5105     case IDM_Engine2Options:\r
5106       savedHwnd = hwnd;\r
5107       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5108       EngineOptionsPopup(hwnd, &second);\r
5109       break;\r
5110 \r
5111     case IDM_OptionsUCI:\r
5112       UciOptionsPopup(hwnd);\r
5113       break;\r
5114 \r
5115     case IDM_IcsOptions:\r
5116       IcsOptionsPopup(hwnd);\r
5117       break;\r
5118 \r
5119     case IDM_Fonts:\r
5120       FontsOptionsPopup(hwnd);\r
5121       break;\r
5122 \r
5123     case IDM_Sounds:\r
5124       SoundOptionsPopup(hwnd);\r
5125       break;\r
5126 \r
5127     case IDM_CommPort:\r
5128       CommPortOptionsPopup(hwnd);\r
5129       break;\r
5130 \r
5131     case IDM_LoadOptions:\r
5132       LoadOptionsPopup(hwnd);\r
5133       break;\r
5134 \r
5135     case IDM_SaveOptions:\r
5136       SaveOptionsPopup(hwnd);\r
5137       break;\r
5138 \r
5139     case IDM_TimeControl:\r
5140       TimeControlOptionsPopup(hwnd);\r
5141       break;\r
5142 \r
5143     case IDM_SaveSettings:\r
5144       SaveSettings(settingsFileName);\r
5145       break;\r
5146 \r
5147     case IDM_SaveSettingsOnExit:\r
5148       saveSettingsOnExit = !saveSettingsOnExit;\r
5149       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5150                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5151                                          MF_CHECKED : MF_UNCHECKED));\r
5152       break;\r
5153 \r
5154     case IDM_Hint:\r
5155       HintEvent();\r
5156       break;\r
5157 \r
5158     case IDM_Book:\r
5159       BookEvent();\r
5160       break;\r
5161 \r
5162     case IDM_AboutGame:\r
5163       AboutGameEvent();\r
5164       break;\r
5165 \r
5166     case IDM_Debug:\r
5167       appData.debugMode = !appData.debugMode;\r
5168       if (appData.debugMode) {\r
5169         char dir[MSG_SIZ];\r
5170         GetCurrentDirectory(MSG_SIZ, dir);\r
5171         SetCurrentDirectory(installDir);\r
5172         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5173         SetCurrentDirectory(dir);\r
5174         setbuf(debugFP, NULL);\r
5175       } else {\r
5176         fclose(debugFP);\r
5177         debugFP = NULL;\r
5178       }\r
5179       break;\r
5180 \r
5181     case IDM_HELPCONTENTS:\r
5182       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5183           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5184           MessageBox (GetFocus(),\r
5185                     _("Unable to activate help"),\r
5186                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5187       }\r
5188       break;\r
5189 \r
5190     case IDM_HELPSEARCH:\r
5191         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5192             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5193         MessageBox (GetFocus(),\r
5194                     _("Unable to activate help"),\r
5195                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5196       }\r
5197       break;\r
5198 \r
5199     case IDM_HELPHELP:\r
5200       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5201         MessageBox (GetFocus(),\r
5202                     _("Unable to activate help"),\r
5203                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5204       }\r
5205       break;\r
5206 \r
5207     case IDM_ABOUT:\r
5208       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5209       DialogBox(hInst, \r
5210         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5211         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5212       FreeProcInstance(lpProc);\r
5213       break;\r
5214 \r
5215     case IDM_DirectCommand1:\r
5216       AskQuestionEvent(_("Direct Command"),\r
5217                        _("Send to chess program:"), "", "1");\r
5218       break;\r
5219     case IDM_DirectCommand2:\r
5220       AskQuestionEvent(_("Direct Command"),\r
5221                        _("Send to second chess program:"), "", "2");\r
5222       break;\r
5223 \r
5224     case EP_WhitePawn:\r
5225       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5226       fromX = fromY = -1;\r
5227       break;\r
5228 \r
5229     case EP_WhiteKnight:\r
5230       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5231       fromX = fromY = -1;\r
5232       break;\r
5233 \r
5234     case EP_WhiteBishop:\r
5235       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5236       fromX = fromY = -1;\r
5237       break;\r
5238 \r
5239     case EP_WhiteRook:\r
5240       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5241       fromX = fromY = -1;\r
5242       break;\r
5243 \r
5244     case EP_WhiteQueen:\r
5245       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5246       fromX = fromY = -1;\r
5247       break;\r
5248 \r
5249     case EP_WhiteFerz:\r
5250       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5251       fromX = fromY = -1;\r
5252       break;\r
5253 \r
5254     case EP_WhiteWazir:\r
5255       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5256       fromX = fromY = -1;\r
5257       break;\r
5258 \r
5259     case EP_WhiteAlfil:\r
5260       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5261       fromX = fromY = -1;\r
5262       break;\r
5263 \r
5264     case EP_WhiteCannon:\r
5265       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5266       fromX = fromY = -1;\r
5267       break;\r
5268 \r
5269     case EP_WhiteCardinal:\r
5270       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5271       fromX = fromY = -1;\r
5272       break;\r
5273 \r
5274     case EP_WhiteMarshall:\r
5275       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5276       fromX = fromY = -1;\r
5277       break;\r
5278 \r
5279     case EP_WhiteKing:\r
5280       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5281       fromX = fromY = -1;\r
5282       break;\r
5283 \r
5284     case EP_BlackPawn:\r
5285       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5286       fromX = fromY = -1;\r
5287       break;\r
5288 \r
5289     case EP_BlackKnight:\r
5290       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5291       fromX = fromY = -1;\r
5292       break;\r
5293 \r
5294     case EP_BlackBishop:\r
5295       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5296       fromX = fromY = -1;\r
5297       break;\r
5298 \r
5299     case EP_BlackRook:\r
5300       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5301       fromX = fromY = -1;\r
5302       break;\r
5303 \r
5304     case EP_BlackQueen:\r
5305       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5306       fromX = fromY = -1;\r
5307       break;\r
5308 \r
5309     case EP_BlackFerz:\r
5310       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5311       fromX = fromY = -1;\r
5312       break;\r
5313 \r
5314     case EP_BlackWazir:\r
5315       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5316       fromX = fromY = -1;\r
5317       break;\r
5318 \r
5319     case EP_BlackAlfil:\r
5320       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5321       fromX = fromY = -1;\r
5322       break;\r
5323 \r
5324     case EP_BlackCannon:\r
5325       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5326       fromX = fromY = -1;\r
5327       break;\r
5328 \r
5329     case EP_BlackCardinal:\r
5330       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5331       fromX = fromY = -1;\r
5332       break;\r
5333 \r
5334     case EP_BlackMarshall:\r
5335       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5336       fromX = fromY = -1;\r
5337       break;\r
5338 \r
5339     case EP_BlackKing:\r
5340       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5341       fromX = fromY = -1;\r
5342       break;\r
5343 \r
5344     case EP_EmptySquare:\r
5345       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5346       fromX = fromY = -1;\r
5347       break;\r
5348 \r
5349     case EP_ClearBoard:\r
5350       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5351       fromX = fromY = -1;\r
5352       break;\r
5353 \r
5354     case EP_White:\r
5355       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5356       fromX = fromY = -1;\r
5357       break;\r
5358 \r
5359     case EP_Black:\r
5360       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5361       fromX = fromY = -1;\r
5362       break;\r
5363 \r
5364     case EP_Promote:\r
5365       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5366       fromX = fromY = -1;\r
5367       break;\r
5368 \r
5369     case EP_Demote:\r
5370       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5371       fromX = fromY = -1;\r
5372       break;\r
5373 \r
5374     case DP_Pawn:\r
5375       DropMenuEvent(WhitePawn, fromX, fromY);\r
5376       fromX = fromY = -1;\r
5377       break;\r
5378 \r
5379     case DP_Knight:\r
5380       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5381       fromX = fromY = -1;\r
5382       break;\r
5383 \r
5384     case DP_Bishop:\r
5385       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5386       fromX = fromY = -1;\r
5387       break;\r
5388 \r
5389     case DP_Rook:\r
5390       DropMenuEvent(WhiteRook, fromX, fromY);\r
5391       fromX = fromY = -1;\r
5392       break;\r
5393 \r
5394     case DP_Queen:\r
5395       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5396       fromX = fromY = -1;\r
5397       break;\r
5398 \r
5399     case IDM_English:\r
5400       barbaric = 0;\r
5401       TranslateMenus(0);\r
5402       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5403       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5404       lastChecked = wmId;\r
5405       break;\r
5406 \r
5407     default:\r
5408       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5409           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5410           TranslateMenus(0);\r
5411           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5412           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5413           lastChecked = wmId;\r
5414           break;\r
5415       }\r
5416       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5417     }\r
5418     break;\r
5419 \r
5420   case WM_TIMER:\r
5421     switch (wParam) {\r
5422     case CLOCK_TIMER_ID:\r
5423       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5424       clockTimerEvent = 0;\r
5425       DecrementClocks(); /* call into back end */\r
5426       break;\r
5427     case LOAD_GAME_TIMER_ID:\r
5428       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5429       loadGameTimerEvent = 0;\r
5430       AutoPlayGameLoop(); /* call into back end */\r
5431       break;\r
5432     case ANALYSIS_TIMER_ID:\r
5433       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5434                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5435         AnalysisPeriodicEvent(0);\r
5436       } else {\r
5437         KillTimer(hwnd, analysisTimerEvent);\r
5438         analysisTimerEvent = 0;\r
5439       }\r
5440       break;\r
5441     case DELAYED_TIMER_ID:\r
5442       KillTimer(hwnd, delayedTimerEvent);\r
5443       delayedTimerEvent = 0;\r
5444       delayedTimerCallback();\r
5445       break;\r
5446     }\r
5447     break;\r
5448 \r
5449   case WM_USER_Input:\r
5450     InputEvent(hwnd, message, wParam, lParam);\r
5451     break;\r
5452 \r
5453   /* [AS] Also move "attached" child windows */\r
5454   case WM_WINDOWPOSCHANGING:\r
5455 \r
5456     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5457         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5458 \r
5459         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5460             /* Window is moving */\r
5461             RECT rcMain;\r
5462 \r
5463 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5464             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5465             rcMain.right  = wpMain.x + wpMain.width;\r
5466             rcMain.top    = wpMain.y;\r
5467             rcMain.bottom = wpMain.y + wpMain.height;\r
5468             \r
5469             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5470             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5471             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5472             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5473             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5474             wpMain.x = lpwp->x;\r
5475             wpMain.y = lpwp->y;\r
5476         }\r
5477     }\r
5478     break;\r
5479 \r
5480   /* [AS] Snapping */\r
5481   case WM_ENTERSIZEMOVE:\r
5482     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5483     if (hwnd == hwndMain) {\r
5484       doingSizing = TRUE;\r
5485       lastSizing = 0;\r
5486     }\r
5487     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5488     break;\r
5489 \r
5490   case WM_SIZING:\r
5491     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5492     if (hwnd == hwndMain) {\r
5493       lastSizing = wParam;\r
5494     }\r
5495     break;\r
5496 \r
5497   case WM_MOVING:\r
5498     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5499       return OnMoving( &sd, hwnd, wParam, lParam );\r
5500 \r
5501   case WM_EXITSIZEMOVE:\r
5502     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5503     if (hwnd == hwndMain) {\r
5504       RECT client;\r
5505       doingSizing = FALSE;\r
5506       InvalidateRect(hwnd, &boardRect, FALSE);\r
5507       GetClientRect(hwnd, &client);\r
5508       ResizeBoard(client.right, client.bottom, lastSizing);\r
5509       lastSizing = 0;\r
5510       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5511     }\r
5512     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5513     break;\r
5514 \r
5515   case WM_DESTROY: /* message: window being destroyed */\r
5516     PostQuitMessage(0);\r
5517     break;\r
5518 \r
5519   case WM_CLOSE:\r
5520     if (hwnd == hwndMain) {\r
5521       ExitEvent(0);\r
5522     }\r
5523     break;\r
5524 \r
5525   default:      /* Passes it on if unprocessed */\r
5526     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5527   }\r
5528   return 0;\r
5529 }\r
5530 \r
5531 /*---------------------------------------------------------------------------*\\r
5532  *\r
5533  * Misc utility routines\r
5534  *\r
5535 \*---------------------------------------------------------------------------*/\r
5536 \r
5537 /*\r
5538  * Decent random number generator, at least not as bad as Windows\r
5539  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5540  */\r
5541 unsigned int randstate;\r
5542 \r
5543 int\r
5544 myrandom(void)\r
5545 {\r
5546   randstate = randstate * 1664525 + 1013904223;\r
5547   return (int) randstate & 0x7fffffff;\r
5548 }\r
5549 \r
5550 void\r
5551 mysrandom(unsigned int seed)\r
5552 {\r
5553   randstate = seed;\r
5554 }\r
5555 \r
5556 \r
5557 /* \r
5558  * returns TRUE if user selects a different color, FALSE otherwise \r
5559  */\r
5560 \r
5561 BOOL\r
5562 ChangeColor(HWND hwnd, COLORREF *which)\r
5563 {\r
5564   static BOOL firstTime = TRUE;\r
5565   static DWORD customColors[16];\r
5566   CHOOSECOLOR cc;\r
5567   COLORREF newcolor;\r
5568   int i;\r
5569   ColorClass ccl;\r
5570 \r
5571   if (firstTime) {\r
5572     /* Make initial colors in use available as custom colors */\r
5573     /* Should we put the compiled-in defaults here instead? */\r
5574     i = 0;\r
5575     customColors[i++] = lightSquareColor & 0xffffff;\r
5576     customColors[i++] = darkSquareColor & 0xffffff;\r
5577     customColors[i++] = whitePieceColor & 0xffffff;\r
5578     customColors[i++] = blackPieceColor & 0xffffff;\r
5579     customColors[i++] = highlightSquareColor & 0xffffff;\r
5580     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5581 \r
5582     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5583       customColors[i++] = textAttribs[ccl].color;\r
5584     }\r
5585     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5586     firstTime = FALSE;\r
5587   }\r
5588 \r
5589   cc.lStructSize = sizeof(cc);\r
5590   cc.hwndOwner = hwnd;\r
5591   cc.hInstance = NULL;\r
5592   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5593   cc.lpCustColors = (LPDWORD) customColors;\r
5594   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5595 \r
5596   if (!ChooseColor(&cc)) return FALSE;\r
5597 \r
5598   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5599   if (newcolor == *which) return FALSE;\r
5600   *which = newcolor;\r
5601   return TRUE;\r
5602 \r
5603   /*\r
5604   InitDrawingColors();\r
5605   InvalidateRect(hwnd, &boardRect, FALSE);\r
5606   */\r
5607 }\r
5608 \r
5609 BOOLEAN\r
5610 MyLoadSound(MySound *ms)\r
5611 {\r
5612   BOOL ok = FALSE;\r
5613   struct stat st;\r
5614   FILE *f;\r
5615 \r
5616   if (ms->data) free(ms->data);\r
5617   ms->data = NULL;\r
5618 \r
5619   switch (ms->name[0]) {\r
5620   case NULLCHAR:\r
5621     /* Silence */\r
5622     ok = TRUE;\r
5623     break;\r
5624   case '$':\r
5625     /* System sound from Control Panel.  Don't preload here. */\r
5626     ok = TRUE;\r
5627     break;\r
5628   case '!':\r
5629     if (ms->name[1] == NULLCHAR) {\r
5630       /* "!" alone = silence */\r
5631       ok = TRUE;\r
5632     } else {\r
5633       /* Builtin wave resource.  Error if not found. */\r
5634       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5635       if (h == NULL) break;\r
5636       ms->data = (void *)LoadResource(hInst, h);\r
5637       if (h == NULL) break;\r
5638       ok = TRUE;\r
5639     }\r
5640     break;\r
5641   default:\r
5642     /* .wav file.  Error if not found. */\r
5643     f = fopen(ms->name, "rb");\r
5644     if (f == NULL) break;\r
5645     if (fstat(fileno(f), &st) < 0) break;\r
5646     ms->data = malloc(st.st_size);\r
5647     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5648     fclose(f);\r
5649     ok = TRUE;\r
5650     break;\r
5651   }\r
5652   if (!ok) {\r
5653     char buf[MSG_SIZ];\r
5654       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5655     DisplayError(buf, GetLastError());\r
5656   }\r
5657   return ok;\r
5658 }\r
5659 \r
5660 BOOLEAN\r
5661 MyPlaySound(MySound *ms)\r
5662 {\r
5663   BOOLEAN ok = FALSE;\r
5664 \r
5665   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5666   switch (ms->name[0]) {\r
5667   case NULLCHAR:\r
5668         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5669     /* Silence */\r
5670     ok = TRUE;\r
5671     break;\r
5672   case '$':\r
5673     /* System sound from Control Panel (deprecated feature).\r
5674        "$" alone or an unset sound name gets default beep (still in use). */\r
5675     if (ms->name[1]) {\r
5676       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5677     }\r
5678     if (!ok) ok = MessageBeep(MB_OK);\r
5679     break; \r
5680   case '!':\r
5681     /* Builtin wave resource, or "!" alone for silence */\r
5682     if (ms->name[1]) {\r
5683       if (ms->data == NULL) return FALSE;\r
5684       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5685     } else {\r
5686       ok = TRUE;\r
5687     }\r
5688     break;\r
5689   default:\r
5690     /* .wav file.  Error if not found. */\r
5691     if (ms->data == NULL) return FALSE;\r
5692     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5693     break;\r
5694   }\r
5695   /* Don't print an error: this can happen innocently if the sound driver\r
5696      is busy; for instance, if another instance of WinBoard is playing\r
5697      a sound at about the same time. */\r
5698   return ok;\r
5699 }\r
5700 \r
5701 \r
5702 LRESULT CALLBACK\r
5703 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5704 {\r
5705   BOOL ok;\r
5706   OPENFILENAME *ofn;\r
5707   static UINT *number; /* gross that this is static */\r
5708 \r
5709   switch (message) {\r
5710   case WM_INITDIALOG: /* message: initialize dialog box */\r
5711     /* Center the dialog over the application window */\r
5712     ofn = (OPENFILENAME *) lParam;\r
5713     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5714       number = (UINT *) ofn->lCustData;\r
5715       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5716     } else {\r
5717       number = NULL;\r
5718     }\r
5719     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5720     Translate(hDlg, 1536);\r
5721     return FALSE;  /* Allow for further processing */\r
5722 \r
5723   case WM_COMMAND:\r
5724     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5725       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5726     }\r
5727     return FALSE;  /* Allow for further processing */\r
5728   }\r
5729   return FALSE;\r
5730 }\r
5731 \r
5732 UINT APIENTRY\r
5733 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5734 {\r
5735   static UINT *number;\r
5736   OPENFILENAME *ofname;\r
5737   OFNOTIFY *ofnot;\r
5738   switch (uiMsg) {\r
5739   case WM_INITDIALOG:\r
5740     Translate(hdlg, DLG_IndexNumber);\r
5741     ofname = (OPENFILENAME *)lParam;\r
5742     number = (UINT *)(ofname->lCustData);\r
5743     break;\r
5744   case WM_NOTIFY:\r
5745     ofnot = (OFNOTIFY *)lParam;\r
5746     if (ofnot->hdr.code == CDN_FILEOK) {\r
5747       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5748     }\r
5749     break;\r
5750   }\r
5751   return 0;\r
5752 }\r
5753 \r
5754 \r
5755 FILE *\r
5756 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5757                char *nameFilt, char *dlgTitle, UINT *number,\r
5758                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5759 {\r
5760   OPENFILENAME openFileName;\r
5761   char buf1[MSG_SIZ];\r
5762   FILE *f;\r
5763 \r
5764   if (fileName == NULL) fileName = buf1;\r
5765   if (defName == NULL) {\r
5766     safeStrCpy(fileName, "*.", 3 );\r
5767     strcat(fileName, defExt);\r
5768   } else {\r
5769     safeStrCpy(fileName, defName, MSG_SIZ );\r
5770   }\r
5771     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5772   if (number) *number = 0;\r
5773 \r
5774   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5775   openFileName.hwndOwner         = hwnd;\r
5776   openFileName.hInstance         = (HANDLE) hInst;\r
5777   openFileName.lpstrFilter       = nameFilt;\r
5778   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5779   openFileName.nMaxCustFilter    = 0L;\r
5780   openFileName.nFilterIndex      = 1L;\r
5781   openFileName.lpstrFile         = fileName;\r
5782   openFileName.nMaxFile          = MSG_SIZ;\r
5783   openFileName.lpstrFileTitle    = fileTitle;\r
5784   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5785   openFileName.lpstrInitialDir   = NULL;\r
5786   openFileName.lpstrTitle        = dlgTitle;\r
5787   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5788     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5789     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5790     | (oldDialog ? 0 : OFN_EXPLORER);\r
5791   openFileName.nFileOffset       = 0;\r
5792   openFileName.nFileExtension    = 0;\r
5793   openFileName.lpstrDefExt       = defExt;\r
5794   openFileName.lCustData         = (LONG) number;\r
5795   openFileName.lpfnHook          = oldDialog ?\r
5796     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5797   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5798 \r
5799   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5800                         GetOpenFileName(&openFileName)) {\r
5801     /* open the file */\r
5802     f = fopen(openFileName.lpstrFile, write);\r
5803     if (f == NULL) {\r
5804       MessageBox(hwnd, _("File open failed"), NULL,\r
5805                  MB_OK|MB_ICONEXCLAMATION);\r
5806       return NULL;\r
5807     }\r
5808   } else {\r
5809     int err = CommDlgExtendedError();\r
5810     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5811     return FALSE;\r
5812   }\r
5813   return f;\r
5814 }\r
5815 \r
5816 \r
5817 \r
5818 VOID APIENTRY\r
5819 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5820 {\r
5821   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5822 \r
5823   /*\r
5824    * Get the first pop-up menu in the menu template. This is the\r
5825    * menu that TrackPopupMenu displays.\r
5826    */\r
5827   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5828   TranslateOneMenu(10, hmenuTrackPopup);\r
5829 \r
5830   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5831 \r
5832   /*\r
5833    * TrackPopup uses screen coordinates, so convert the\r
5834    * coordinates of the mouse click to screen coordinates.\r
5835    */\r
5836   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5837 \r
5838   /* Draw and track the floating pop-up menu. */\r
5839   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5840                  pt.x, pt.y, 0, hwnd, NULL);\r
5841 \r
5842   /* Destroy the menu.*/\r
5843   DestroyMenu(hmenu);\r
5844 }\r
5845    \r
5846 typedef struct {\r
5847   HWND hDlg, hText;\r
5848   int sizeX, sizeY, newSizeX, newSizeY;\r
5849   HDWP hdwp;\r
5850 } ResizeEditPlusButtonsClosure;\r
5851 \r
5852 BOOL CALLBACK\r
5853 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5854 {\r
5855   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5856   RECT rect;\r
5857   POINT pt;\r
5858 \r
5859   if (hChild == cl->hText) return TRUE;\r
5860   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5861   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5862   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5863   ScreenToClient(cl->hDlg, &pt);\r
5864   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5865     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5866   return TRUE;\r
5867 }\r
5868 \r
5869 /* Resize a dialog that has a (rich) edit field filling most of\r
5870    the top, with a row of buttons below */\r
5871 VOID\r
5872 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5873 {\r
5874   RECT rectText;\r
5875   int newTextHeight, newTextWidth;\r
5876   ResizeEditPlusButtonsClosure cl;\r
5877   \r
5878   /*if (IsIconic(hDlg)) return;*/\r
5879   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5880   \r
5881   cl.hdwp = BeginDeferWindowPos(8);\r
5882 \r
5883   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5884   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5885   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5886   if (newTextHeight < 0) {\r
5887     newSizeY += -newTextHeight;\r
5888     newTextHeight = 0;\r
5889   }\r
5890   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5891     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5892 \r
5893   cl.hDlg = hDlg;\r
5894   cl.hText = hText;\r
5895   cl.sizeX = sizeX;\r
5896   cl.sizeY = sizeY;\r
5897   cl.newSizeX = newSizeX;\r
5898   cl.newSizeY = newSizeY;\r
5899   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5900 \r
5901   EndDeferWindowPos(cl.hdwp);\r
5902 }\r
5903 \r
5904 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5905 {\r
5906     RECT    rChild, rParent;\r
5907     int     wChild, hChild, wParent, hParent;\r
5908     int     wScreen, hScreen, xNew, yNew;\r
5909     HDC     hdc;\r
5910 \r
5911     /* Get the Height and Width of the child window */\r
5912     GetWindowRect (hwndChild, &rChild);\r
5913     wChild = rChild.right - rChild.left;\r
5914     hChild = rChild.bottom - rChild.top;\r
5915 \r
5916     /* Get the Height and Width of the parent window */\r
5917     GetWindowRect (hwndParent, &rParent);\r
5918     wParent = rParent.right - rParent.left;\r
5919     hParent = rParent.bottom - rParent.top;\r
5920 \r
5921     /* Get the display limits */\r
5922     hdc = GetDC (hwndChild);\r
5923     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5924     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5925     ReleaseDC(hwndChild, hdc);\r
5926 \r
5927     /* Calculate new X position, then adjust for screen */\r
5928     xNew = rParent.left + ((wParent - wChild) /2);\r
5929     if (xNew < 0) {\r
5930         xNew = 0;\r
5931     } else if ((xNew+wChild) > wScreen) {\r
5932         xNew = wScreen - wChild;\r
5933     }\r
5934 \r
5935     /* Calculate new Y position, then adjust for screen */\r
5936     if( mode == 0 ) {\r
5937         yNew = rParent.top  + ((hParent - hChild) /2);\r
5938     }\r
5939     else {\r
5940         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5941     }\r
5942 \r
5943     if (yNew < 0) {\r
5944         yNew = 0;\r
5945     } else if ((yNew+hChild) > hScreen) {\r
5946         yNew = hScreen - hChild;\r
5947     }\r
5948 \r
5949     /* Set it, and return */\r
5950     return SetWindowPos (hwndChild, NULL,\r
5951                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5952 }\r
5953 \r
5954 /* Center one window over another */\r
5955 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5956 {\r
5957     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5958 }\r
5959 \r
5960 /*---------------------------------------------------------------------------*\\r
5961  *\r
5962  * Startup Dialog functions\r
5963  *\r
5964 \*---------------------------------------------------------------------------*/\r
5965 void\r
5966 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5967 {\r
5968   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5969 \r
5970   while (*cd != NULL) {\r
5971     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5972     cd++;\r
5973   }\r
5974 }\r
5975 \r
5976 void\r
5977 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5978 {\r
5979   char buf1[MAX_ARG_LEN];\r
5980   int len;\r
5981 \r
5982   if (str[0] == '@') {\r
5983     FILE* f = fopen(str + 1, "r");\r
5984     if (f == NULL) {\r
5985       DisplayFatalError(str + 1, errno, 2);\r
5986       return;\r
5987     }\r
5988     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5989     fclose(f);\r
5990     buf1[len] = NULLCHAR;\r
5991     str = buf1;\r
5992   }\r
5993 \r
5994   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5995 \r
5996   for (;;) {\r
5997     char buf[MSG_SIZ];\r
5998     char *end = strchr(str, '\n');\r
5999     if (end == NULL) return;\r
6000     memcpy(buf, str, end - str);\r
6001     buf[end - str] = NULLCHAR;\r
6002     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6003     str = end + 1;\r
6004   }\r
6005 }\r
6006 \r
6007 void\r
6008 SetStartupDialogEnables(HWND hDlg)\r
6009 {\r
6010   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6011     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6012     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6013   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6014     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6015   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6016     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6017   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6018     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6019   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6020     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6021     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6022     IsDlgButtonChecked(hDlg, OPT_View));\r
6023 }\r
6024 \r
6025 char *\r
6026 QuoteForFilename(char *filename)\r
6027 {\r
6028   int dquote, space;\r
6029   dquote = strchr(filename, '"') != NULL;\r
6030   space = strchr(filename, ' ') != NULL;\r
6031   if (dquote || space) {\r
6032     if (dquote) {\r
6033       return "'";\r
6034     } else {\r
6035       return "\"";\r
6036     }\r
6037   } else {\r
6038     return "";\r
6039   }\r
6040 }\r
6041 \r
6042 VOID\r
6043 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6044 {\r
6045   char buf[MSG_SIZ];\r
6046   char *q;\r
6047 \r
6048   InitComboStringsFromOption(hwndCombo, nthnames);\r
6049   q = QuoteForFilename(nthcp);\r
6050     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6051   if (*nthdir != NULLCHAR) {\r
6052     q = QuoteForFilename(nthdir);\r
6053       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6054   }\r
6055   if (*nthcp == NULLCHAR) {\r
6056     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6057   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6058     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6059     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6060   }\r
6061 }\r
6062 \r
6063 LRESULT CALLBACK\r
6064 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6065 {\r
6066   char buf[MSG_SIZ];\r
6067   HANDLE hwndCombo;\r
6068   char *p;\r
6069 \r
6070   switch (message) {\r
6071   case WM_INITDIALOG:\r
6072     /* Center the dialog */\r
6073     CenterWindow (hDlg, GetDesktopWindow());\r
6074     Translate(hDlg, DLG_Startup);\r
6075     /* Initialize the dialog items */\r
6076     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6077                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6078                   firstChessProgramNames);\r
6079     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6080                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6081                   secondChessProgramNames);\r
6082     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6083     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6084       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6085     if (*appData.icsHelper != NULLCHAR) {\r
6086       char *q = QuoteForFilename(appData.icsHelper);\r
6087       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6088     }\r
6089     if (*appData.icsHost == NULLCHAR) {\r
6090       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6091       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6092     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6093       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6094       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6095     }\r
6096 \r
6097     if (appData.icsActive) {\r
6098       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6099     }\r
6100     else if (appData.noChessProgram) {\r
6101       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6102     }\r
6103     else {\r
6104       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6105     }\r
6106 \r
6107     SetStartupDialogEnables(hDlg);\r
6108     return TRUE;\r
6109 \r
6110   case WM_COMMAND:\r
6111     switch (LOWORD(wParam)) {\r
6112     case IDOK:\r
6113       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6114         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6115         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6116         p = buf;\r
6117         ParseArgs(StringGet, &p);\r
6118         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6119         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6120         p = buf;\r
6121         ParseArgs(StringGet, &p);\r
6122         appData.noChessProgram = FALSE;\r
6123         appData.icsActive = FALSE;\r
6124       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6125         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6126         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6127         p = buf;\r
6128         ParseArgs(StringGet, &p);\r
6129         if (appData.zippyPlay) {\r
6130           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6131           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6132           p = buf;\r
6133           ParseArgs(StringGet, &p);\r
6134         }\r
6135       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6136         appData.noChessProgram = TRUE;\r
6137         appData.icsActive = FALSE;\r
6138       } else {\r
6139         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6140                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6141         return TRUE;\r
6142       }\r
6143       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6144         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6145         p = buf;\r
6146         ParseArgs(StringGet, &p);\r
6147       }\r
6148       EndDialog(hDlg, TRUE);\r
6149       return TRUE;\r
6150 \r
6151     case IDCANCEL:\r
6152       ExitEvent(0);\r
6153       return TRUE;\r
6154 \r
6155     case IDM_HELPCONTENTS:\r
6156       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6157         MessageBox (GetFocus(),\r
6158                     _("Unable to activate help"),\r
6159                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6160       }\r
6161       break;\r
6162 \r
6163     default:\r
6164       SetStartupDialogEnables(hDlg);\r
6165       break;\r
6166     }\r
6167     break;\r
6168   }\r
6169   return FALSE;\r
6170 }\r
6171 \r
6172 /*---------------------------------------------------------------------------*\\r
6173  *\r
6174  * About box dialog functions\r
6175  *\r
6176 \*---------------------------------------------------------------------------*/\r
6177 \r
6178 /* Process messages for "About" dialog box */\r
6179 LRESULT CALLBACK\r
6180 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6181 {\r
6182   switch (message) {\r
6183   case WM_INITDIALOG: /* message: initialize dialog box */\r
6184     /* Center the dialog over the application window */\r
6185     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6186     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6187     Translate(hDlg, ABOUTBOX);\r
6188     JAWS_COPYRIGHT\r
6189     return (TRUE);\r
6190 \r
6191   case WM_COMMAND: /* message: received a command */\r
6192     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6193         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6194       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6195       return (TRUE);\r
6196     }\r
6197     break;\r
6198   }\r
6199   return (FALSE);\r
6200 }\r
6201 \r
6202 /*---------------------------------------------------------------------------*\\r
6203  *\r
6204  * Comment Dialog functions\r
6205  *\r
6206 \*---------------------------------------------------------------------------*/\r
6207 \r
6208 LRESULT CALLBACK\r
6209 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6210 {\r
6211   static HANDLE hwndText = NULL;\r
6212   int len, newSizeX, newSizeY, flags;\r
6213   static int sizeX, sizeY;\r
6214   char *str;\r
6215   RECT rect;\r
6216   MINMAXINFO *mmi;\r
6217 \r
6218   switch (message) {\r
6219   case WM_INITDIALOG: /* message: initialize dialog box */\r
6220     /* Initialize the dialog items */\r
6221     Translate(hDlg, DLG_EditComment);\r
6222     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6223     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6224     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6225     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6226     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6227     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6228     SetWindowText(hDlg, commentTitle);\r
6229     if (editComment) {\r
6230       SetFocus(hwndText);\r
6231     } else {\r
6232       SetFocus(GetDlgItem(hDlg, IDOK));\r
6233     }\r
6234     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6235                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6236                 MAKELPARAM(FALSE, 0));\r
6237     /* Size and position the dialog */\r
6238     if (!commentDialog) {\r
6239       commentDialog = hDlg;\r
6240       flags = SWP_NOZORDER;\r
6241       GetClientRect(hDlg, &rect);\r
6242       sizeX = rect.right;\r
6243       sizeY = rect.bottom;\r
6244       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6245           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6246         WINDOWPLACEMENT wp;\r
6247         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6248         wp.length = sizeof(WINDOWPLACEMENT);\r
6249         wp.flags = 0;\r
6250         wp.showCmd = SW_SHOW;\r
6251         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6252         wp.rcNormalPosition.left = wpComment.x;\r
6253         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6254         wp.rcNormalPosition.top = wpComment.y;\r
6255         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6256         SetWindowPlacement(hDlg, &wp);\r
6257 \r
6258         GetClientRect(hDlg, &rect);\r
6259         newSizeX = rect.right;\r
6260         newSizeY = rect.bottom;\r
6261         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6262                               newSizeX, newSizeY);\r
6263         sizeX = newSizeX;\r
6264         sizeY = newSizeY;\r
6265       }\r
6266     }\r
6267     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6268     return FALSE;\r
6269 \r
6270   case WM_COMMAND: /* message: received a command */\r
6271     switch (LOWORD(wParam)) {\r
6272     case IDOK:\r
6273       if (editComment) {\r
6274         char *p, *q;\r
6275         /* Read changed options from the dialog box */\r
6276         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6277         len = GetWindowTextLength(hwndText);\r
6278         str = (char *) malloc(len + 1);\r
6279         GetWindowText(hwndText, str, len + 1);\r
6280         p = q = str;\r
6281         while (*q) {\r
6282           if (*q == '\r')\r
6283             q++;\r
6284           else\r
6285             *p++ = *q++;\r
6286         }\r
6287         *p = NULLCHAR;\r
6288         ReplaceComment(commentIndex, str);\r
6289         free(str);\r
6290       }\r
6291       CommentPopDown();\r
6292       return TRUE;\r
6293 \r
6294     case IDCANCEL:\r
6295     case OPT_CancelComment:\r
6296       CommentPopDown();\r
6297       return TRUE;\r
6298 \r
6299     case OPT_ClearComment:\r
6300       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6301       break;\r
6302 \r
6303     case OPT_EditComment:\r
6304       EditCommentEvent();\r
6305       return TRUE;\r
6306 \r
6307     default:\r
6308       break;\r
6309     }\r
6310     break;\r
6311 \r
6312   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6313         if( wParam == OPT_CommentText ) {\r
6314             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6315 \r
6316             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6317                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6318                 POINTL pt;\r
6319                 LRESULT index;\r
6320 \r
6321                 pt.x = LOWORD( lpMF->lParam );\r
6322                 pt.y = HIWORD( lpMF->lParam );\r
6323 \r
6324                 if(lpMF->msg == WM_CHAR) {\r
6325                         CHARRANGE sel;\r
6326                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6327                         index = sel.cpMin;\r
6328                 } else\r
6329                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6330 \r
6331                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6332                 len = GetWindowTextLength(hwndText);\r
6333                 str = (char *) malloc(len + 1);\r
6334                 GetWindowText(hwndText, str, len + 1);\r
6335                 ReplaceComment(commentIndex, str);\r
6336                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6337                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6338                 free(str);\r
6339 \r
6340                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6341                 lpMF->msg = WM_USER;\r
6342 \r
6343                 return TRUE;\r
6344             }\r
6345         }\r
6346         break;\r
6347 \r
6348   case WM_SIZE:\r
6349     newSizeX = LOWORD(lParam);\r
6350     newSizeY = HIWORD(lParam);\r
6351     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6352     sizeX = newSizeX;\r
6353     sizeY = newSizeY;\r
6354     break;\r
6355 \r
6356   case WM_GETMINMAXINFO:\r
6357     /* Prevent resizing window too small */\r
6358     mmi = (MINMAXINFO *) lParam;\r
6359     mmi->ptMinTrackSize.x = 100;\r
6360     mmi->ptMinTrackSize.y = 100;\r
6361     break;\r
6362   }\r
6363   return FALSE;\r
6364 }\r
6365 \r
6366 VOID\r
6367 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6368 {\r
6369   FARPROC lpProc;\r
6370   char *p, *q;\r
6371 \r
6372   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6373 \r
6374   if (str == NULL) str = "";\r
6375   p = (char *) malloc(2 * strlen(str) + 2);\r
6376   q = p;\r
6377   while (*str) {\r
6378     if (*str == '\n') *q++ = '\r';\r
6379     *q++ = *str++;\r
6380   }\r
6381   *q = NULLCHAR;\r
6382   if (commentText != NULL) free(commentText);\r
6383 \r
6384   commentIndex = index;\r
6385   commentTitle = title;\r
6386   commentText = p;\r
6387   editComment = edit;\r
6388 \r
6389   if (commentDialog) {\r
6390     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6391     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6392   } else {\r
6393     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6394     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6395                  hwndMain, (DLGPROC)lpProc);\r
6396     FreeProcInstance(lpProc);\r
6397   }\r
6398   commentUp = TRUE;\r
6399 }\r
6400 \r
6401 \r
6402 /*---------------------------------------------------------------------------*\\r
6403  *\r
6404  * Type-in move dialog functions\r
6405  * \r
6406 \*---------------------------------------------------------------------------*/\r
6407 \r
6408 LRESULT CALLBACK\r
6409 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6410 {\r
6411   char move[MSG_SIZ];\r
6412   HWND hInput;\r
6413   ChessMove moveType;\r
6414   int fromX, fromY, toX, toY;\r
6415   char promoChar;\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));\r
6435       { int n; Board board;\r
6436         // [HGM] FENedit\r
6437         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6438                 EditPositionPasteFEN(move);\r
6439                 EndDialog(hDlg, TRUE);\r
6440                 return TRUE;\r
6441         }\r
6442         // [HGM] movenum: allow move number to be typed in any mode\r
6443         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6444           ToNrEvent(2*n-1);\r
6445           EndDialog(hDlg, TRUE);\r
6446           return TRUE;\r
6447         }\r
6448       }\r
6449       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6450         gameMode != Training) {\r
6451         DisplayMoveError(_("Displayed move is not current"));\r
6452       } else {\r
6453 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6454         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6455           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6456         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6457         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6458           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6459           if (gameMode != Training)\r
6460               forwardMostMove = currentMove;\r
6461           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6462         } else {\r
6463           DisplayMoveError(_("Could not parse move"));\r
6464         }\r
6465       }\r
6466       EndDialog(hDlg, TRUE);\r
6467       return TRUE;\r
6468     case IDCANCEL:\r
6469       EndDialog(hDlg, FALSE);\r
6470       return TRUE;\r
6471     default:\r
6472       break;\r
6473     }\r
6474     break;\r
6475   }\r
6476   return FALSE;\r
6477 }\r
6478 \r
6479 VOID\r
6480 PopUpMoveDialog(char firstchar)\r
6481 {\r
6482     FARPROC lpProc;\r
6483     \r
6484     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6485         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6486         gameMode == AnalyzeMode || gameMode == EditGame || \r
6487         gameMode == EditPosition || gameMode == IcsExamining ||\r
6488         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6489         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6490                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6491                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6492         gameMode == Training) {\r
6493       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6494       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6495         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6496       FreeProcInstance(lpProc);\r
6497     }\r
6498 }\r
6499 \r
6500 /*---------------------------------------------------------------------------*\\r
6501  *\r
6502  * Type-in name dialog functions\r
6503  * \r
6504 \*---------------------------------------------------------------------------*/\r
6505 \r
6506 LRESULT CALLBACK\r
6507 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6508 {\r
6509   char move[MSG_SIZ];\r
6510   HWND hInput;\r
6511 \r
6512   switch (message) {\r
6513   case WM_INITDIALOG:\r
6514     move[0] = (char) lParam;\r
6515     move[1] = NULLCHAR;\r
6516     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6517     Translate(hDlg, DLG_TypeInName);\r
6518     hInput = GetDlgItem(hDlg, OPT_Name);\r
6519     SetWindowText(hInput, move);\r
6520     SetFocus(hInput);\r
6521     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6522     return FALSE;\r
6523 \r
6524   case WM_COMMAND:\r
6525     switch (LOWORD(wParam)) {\r
6526     case IDOK:\r
6527       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6528       appData.userName = strdup(move);\r
6529       SetUserLogo();\r
6530       SetGameInfo();\r
6531       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6532         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6533         DisplayTitle(move);\r
6534       }\r
6535 \r
6536 \r
6537       EndDialog(hDlg, TRUE);\r
6538       return TRUE;\r
6539     case IDCANCEL:\r
6540       EndDialog(hDlg, FALSE);\r
6541       return TRUE;\r
6542     default:\r
6543       break;\r
6544     }\r
6545     break;\r
6546   }\r
6547   return FALSE;\r
6548 }\r
6549 \r
6550 VOID\r
6551 PopUpNameDialog(char firstchar)\r
6552 {\r
6553     FARPROC lpProc;\r
6554     \r
6555       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6556       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6557         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6558       FreeProcInstance(lpProc);\r
6559 }\r
6560 \r
6561 /*---------------------------------------------------------------------------*\\r
6562  *\r
6563  *  Error dialogs\r
6564  * \r
6565 \*---------------------------------------------------------------------------*/\r
6566 \r
6567 /* Nonmodal error box */\r
6568 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6569                              WPARAM wParam, LPARAM lParam);\r
6570 \r
6571 VOID\r
6572 ErrorPopUp(char *title, char *content)\r
6573 {\r
6574   FARPROC lpProc;\r
6575   char *p, *q;\r
6576   BOOLEAN modal = hwndMain == NULL;\r
6577 \r
6578   p = content;\r
6579   q = errorMessage;\r
6580   while (*p) {\r
6581     if (*p == '\n') {\r
6582       if (modal) {\r
6583         *q++ = ' ';\r
6584         p++;\r
6585       } else {\r
6586         *q++ = '\r';\r
6587         *q++ = *p++;\r
6588       }\r
6589     } else {\r
6590       *q++ = *p++;\r
6591     }\r
6592   }\r
6593   *q = NULLCHAR;\r
6594   strncpy(errorTitle, title, sizeof(errorTitle));\r
6595   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6596   \r
6597   if (modal) {\r
6598     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6599   } else {\r
6600     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6601     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6602                  hwndMain, (DLGPROC)lpProc);\r
6603     FreeProcInstance(lpProc);\r
6604   }\r
6605 }\r
6606 \r
6607 VOID\r
6608 ErrorPopDown()\r
6609 {\r
6610   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6611   if (errorDialog == NULL) return;\r
6612   DestroyWindow(errorDialog);\r
6613   errorDialog = NULL;\r
6614   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6615 }\r
6616 \r
6617 LRESULT CALLBACK\r
6618 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6619 {\r
6620   HANDLE hwndText;\r
6621   RECT rChild;\r
6622 \r
6623   switch (message) {\r
6624   case WM_INITDIALOG:\r
6625     GetWindowRect(hDlg, &rChild);\r
6626 \r
6627     /*\r
6628     SetWindowPos(hDlg, NULL, rChild.left,\r
6629       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6630       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6631     */\r
6632 \r
6633     /* \r
6634         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6635         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6636         and it doesn't work when you resize the dialog.\r
6637         For now, just give it a default position.\r
6638     */\r
6639     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6640     Translate(hDlg, DLG_Error);\r
6641 \r
6642     errorDialog = hDlg;\r
6643     SetWindowText(hDlg, errorTitle);\r
6644     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6645     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6646     return FALSE;\r
6647 \r
6648   case WM_COMMAND:\r
6649     switch (LOWORD(wParam)) {\r
6650     case IDOK:\r
6651     case IDCANCEL:\r
6652       if (errorDialog == hDlg) errorDialog = NULL;\r
6653       DestroyWindow(hDlg);\r
6654       return TRUE;\r
6655 \r
6656     default:\r
6657       break;\r
6658     }\r
6659     break;\r
6660   }\r
6661   return FALSE;\r
6662 }\r
6663 \r
6664 #ifdef GOTHIC\r
6665 HWND gothicDialog = NULL;\r
6666 \r
6667 LRESULT CALLBACK\r
6668 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6669 {\r
6670   HANDLE hwndText;\r
6671   RECT rChild;\r
6672   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6673 \r
6674   switch (message) {\r
6675   case WM_INITDIALOG:\r
6676     GetWindowRect(hDlg, &rChild);\r
6677 \r
6678     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6679                                                              SWP_NOZORDER);\r
6680 \r
6681     /* \r
6682         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6683         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6684         and it doesn't work when you resize the dialog.\r
6685         For now, just give it a default position.\r
6686     */\r
6687     gothicDialog = hDlg;\r
6688     SetWindowText(hDlg, errorTitle);\r
6689     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6690     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6691     return FALSE;\r
6692 \r
6693   case WM_COMMAND:\r
6694     switch (LOWORD(wParam)) {\r
6695     case IDOK:\r
6696     case IDCANCEL:\r
6697       if (errorDialog == hDlg) errorDialog = NULL;\r
6698       DestroyWindow(hDlg);\r
6699       return TRUE;\r
6700 \r
6701     default:\r
6702       break;\r
6703     }\r
6704     break;\r
6705   }\r
6706   return FALSE;\r
6707 }\r
6708 \r
6709 VOID\r
6710 GothicPopUp(char *title, VariantClass variant)\r
6711 {\r
6712   FARPROC lpProc;\r
6713   static char *lastTitle;\r
6714 \r
6715   strncpy(errorTitle, title, sizeof(errorTitle));\r
6716   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6717 \r
6718   if(lastTitle != title && gothicDialog != NULL) {\r
6719     DestroyWindow(gothicDialog);\r
6720     gothicDialog = NULL;\r
6721   }\r
6722   if(variant != VariantNormal && gothicDialog == NULL) {\r
6723     title = lastTitle;\r
6724     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6725     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6726                  hwndMain, (DLGPROC)lpProc);\r
6727     FreeProcInstance(lpProc);\r
6728   }\r
6729 }\r
6730 #endif\r
6731 \r
6732 /*---------------------------------------------------------------------------*\\r
6733  *\r
6734  *  Ics Interaction console functions\r
6735  *\r
6736 \*---------------------------------------------------------------------------*/\r
6737 \r
6738 #define HISTORY_SIZE 64\r
6739 static char *history[HISTORY_SIZE];\r
6740 int histIn = 0, histP = 0;\r
6741 \r
6742 VOID\r
6743 SaveInHistory(char *cmd)\r
6744 {\r
6745   if (history[histIn] != NULL) {\r
6746     free(history[histIn]);\r
6747     history[histIn] = NULL;\r
6748   }\r
6749   if (*cmd == NULLCHAR) return;\r
6750   history[histIn] = StrSave(cmd);\r
6751   histIn = (histIn + 1) % HISTORY_SIZE;\r
6752   if (history[histIn] != NULL) {\r
6753     free(history[histIn]);\r
6754     history[histIn] = NULL;\r
6755   }\r
6756   histP = histIn;\r
6757 }\r
6758 \r
6759 char *\r
6760 PrevInHistory(char *cmd)\r
6761 {\r
6762   int newhp;\r
6763   if (histP == histIn) {\r
6764     if (history[histIn] != NULL) free(history[histIn]);\r
6765     history[histIn] = StrSave(cmd);\r
6766   }\r
6767   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6768   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6769   histP = newhp;\r
6770   return history[histP];\r
6771 }\r
6772 \r
6773 char *\r
6774 NextInHistory()\r
6775 {\r
6776   if (histP == histIn) return NULL;\r
6777   histP = (histP + 1) % HISTORY_SIZE;\r
6778   return history[histP];   \r
6779 }\r
6780 \r
6781 HMENU\r
6782 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6783 {\r
6784   HMENU hmenu, h;\r
6785   int i = 0;\r
6786   hmenu = LoadMenu(hInst, "TextMenu");\r
6787   h = GetSubMenu(hmenu, 0);\r
6788   while (e->item) {\r
6789     if (strcmp(e->item, "-") == 0) {\r
6790       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6791     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6792       int flags = MF_STRING, j = 0;\r
6793       if (e->item[0] == '|') {\r
6794         flags |= MF_MENUBARBREAK;\r
6795         j++;\r
6796       }\r
6797       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6798       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6799     }\r
6800     e++;\r
6801     i++;\r
6802   } \r
6803   return hmenu;\r
6804 }\r
6805 \r
6806 WNDPROC consoleTextWindowProc;\r
6807 \r
6808 void\r
6809 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6810 {\r
6811   char buf[MSG_SIZ], name[MSG_SIZ];\r
6812   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6813   CHARRANGE sel;\r
6814 \r
6815   if (!getname) {\r
6816     SetWindowText(hInput, command);\r
6817     if (immediate) {\r
6818       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6819     } else {\r
6820       sel.cpMin = 999999;\r
6821       sel.cpMax = 999999;\r
6822       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6823       SetFocus(hInput);\r
6824     }\r
6825     return;\r
6826   }    \r
6827   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6828   if (sel.cpMin == sel.cpMax) {\r
6829     /* Expand to surrounding word */\r
6830     TEXTRANGE tr;\r
6831     do {\r
6832       tr.chrg.cpMax = sel.cpMin;\r
6833       tr.chrg.cpMin = --sel.cpMin;\r
6834       if (sel.cpMin < 0) break;\r
6835       tr.lpstrText = name;\r
6836       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6837     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6838     sel.cpMin++;\r
6839 \r
6840     do {\r
6841       tr.chrg.cpMin = sel.cpMax;\r
6842       tr.chrg.cpMax = ++sel.cpMax;\r
6843       tr.lpstrText = name;\r
6844       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6845     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6846     sel.cpMax--;\r
6847 \r
6848     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6849       MessageBeep(MB_ICONEXCLAMATION);\r
6850       return;\r
6851     }\r
6852     tr.chrg = sel;\r
6853     tr.lpstrText = name;\r
6854     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6855   } else {\r
6856     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6857       MessageBeep(MB_ICONEXCLAMATION);\r
6858       return;\r
6859     }\r
6860     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6861   }\r
6862   if (immediate) {\r
6863     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6864     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6865     SetWindowText(hInput, buf);\r
6866     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6867   } else {\r
6868     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6869       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6870     SetWindowText(hInput, buf);\r
6871     sel.cpMin = 999999;\r
6872     sel.cpMax = 999999;\r
6873     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6874     SetFocus(hInput);\r
6875   }\r
6876 }\r
6877 \r
6878 LRESULT CALLBACK \r
6879 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6880 {\r
6881   HWND hInput;\r
6882   CHARRANGE sel;\r
6883 \r
6884   switch (message) {\r
6885   case WM_KEYDOWN:\r
6886     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6887     if(wParam=='R') return 0;\r
6888     switch (wParam) {\r
6889     case VK_PRIOR:\r
6890       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6891       return 0;\r
6892     case VK_NEXT:\r
6893       sel.cpMin = 999999;\r
6894       sel.cpMax = 999999;\r
6895       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6896       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6897       return 0;\r
6898     }\r
6899     break;\r
6900   case WM_CHAR:\r
6901    if(wParam != '\022') {\r
6902     if (wParam == '\t') {\r
6903       if (GetKeyState(VK_SHIFT) < 0) {\r
6904         /* shifted */\r
6905         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6906         if (buttonDesc[0].hwnd) {\r
6907           SetFocus(buttonDesc[0].hwnd);\r
6908         } else {\r
6909           SetFocus(hwndMain);\r
6910         }\r
6911       } else {\r
6912         /* unshifted */\r
6913         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6914       }\r
6915     } else {\r
6916       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6917       JAWS_DELETE( SetFocus(hInput); )\r
6918       SendMessage(hInput, message, wParam, lParam);\r
6919     }\r
6920     return 0;\r
6921    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6922    lParam = -1;\r
6923   case WM_RBUTTONDOWN:\r
6924     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6925       /* Move selection here if it was empty */\r
6926       POINT pt;\r
6927       pt.x = LOWORD(lParam);\r
6928       pt.y = HIWORD(lParam);\r
6929       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6930       if (sel.cpMin == sel.cpMax) {\r
6931         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6932         sel.cpMax = sel.cpMin;\r
6933         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6934       }\r
6935       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6936 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6937       POINT pt;\r
6938       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6939       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6940       if (sel.cpMin == sel.cpMax) {\r
6941         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6942         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6943       }\r
6944       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6945         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6946       }\r
6947       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6948       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6949       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6950       MenuPopup(hwnd, pt, hmenu, -1);\r
6951 }\r
6952     }\r
6953     return 0;\r
6954   case WM_RBUTTONUP:\r
6955     if (GetKeyState(VK_SHIFT) & ~1) {\r
6956       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6957         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6958     }\r
6959     return 0;\r
6960   case WM_PASTE:\r
6961     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6962     SetFocus(hInput);\r
6963     return SendMessage(hInput, message, wParam, lParam);\r
6964   case WM_MBUTTONDOWN:\r
6965     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6966   case WM_COMMAND:\r
6967     switch (LOWORD(wParam)) {\r
6968     case IDM_QuickPaste:\r
6969       {\r
6970         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6971         if (sel.cpMin == sel.cpMax) {\r
6972           MessageBeep(MB_ICONEXCLAMATION);\r
6973           return 0;\r
6974         }\r
6975         SendMessage(hwnd, WM_COPY, 0, 0);\r
6976         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6977         SendMessage(hInput, WM_PASTE, 0, 0);\r
6978         SetFocus(hInput);\r
6979         return 0;\r
6980       }\r
6981     case IDM_Cut:\r
6982       SendMessage(hwnd, WM_CUT, 0, 0);\r
6983       return 0;\r
6984     case IDM_Paste:\r
6985       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6986       return 0;\r
6987     case IDM_Copy:\r
6988       SendMessage(hwnd, WM_COPY, 0, 0);\r
6989       return 0;\r
6990     default:\r
6991       {\r
6992         int i = LOWORD(wParam) - IDM_CommandX;\r
6993         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6994             icsTextMenuEntry[i].command != NULL) {\r
6995           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6996                    icsTextMenuEntry[i].getname,\r
6997                    icsTextMenuEntry[i].immediate);\r
6998           return 0;\r
6999         }\r
7000       }\r
7001       break;\r
7002     }\r
7003     break;\r
7004   }\r
7005   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7006 }\r
7007 \r
7008 WNDPROC consoleInputWindowProc;\r
7009 \r
7010 LRESULT CALLBACK\r
7011 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7012 {\r
7013   char buf[MSG_SIZ];\r
7014   char *p;\r
7015   static BOOL sendNextChar = FALSE;\r
7016   static BOOL quoteNextChar = FALSE;\r
7017   InputSource *is = consoleInputSource;\r
7018   CHARFORMAT cf;\r
7019   CHARRANGE sel;\r
7020 \r
7021   switch (message) {\r
7022   case WM_CHAR:\r
7023     if (!appData.localLineEditing || sendNextChar) {\r
7024       is->buf[0] = (CHAR) wParam;\r
7025       is->count = 1;\r
7026       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7027       sendNextChar = FALSE;\r
7028       return 0;\r
7029     }\r
7030     if (quoteNextChar) {\r
7031       buf[0] = (char) wParam;\r
7032       buf[1] = NULLCHAR;\r
7033       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7034       quoteNextChar = FALSE;\r
7035       return 0;\r
7036     }\r
7037     switch (wParam) {\r
7038     case '\r':   /* Enter key */\r
7039       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7040       if (consoleEcho) SaveInHistory(is->buf);\r
7041       is->buf[is->count++] = '\n';\r
7042       is->buf[is->count] = NULLCHAR;\r
7043       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7044       if (consoleEcho) {\r
7045         ConsoleOutput(is->buf, is->count, TRUE);\r
7046       } else if (appData.localLineEditing) {\r
7047         ConsoleOutput("\n", 1, TRUE);\r
7048       }\r
7049       /* fall thru */\r
7050     case '\033': /* Escape key */\r
7051       SetWindowText(hwnd, "");\r
7052       cf.cbSize = sizeof(CHARFORMAT);\r
7053       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7054       if (consoleEcho) {\r
7055         cf.crTextColor = textAttribs[ColorNormal].color;\r
7056       } else {\r
7057         cf.crTextColor = COLOR_ECHOOFF;\r
7058       }\r
7059       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7060       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7061       return 0;\r
7062     case '\t':   /* Tab key */\r
7063       if (GetKeyState(VK_SHIFT) < 0) {\r
7064         /* shifted */\r
7065         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7066       } else {\r
7067         /* unshifted */\r
7068         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7069         if (buttonDesc[0].hwnd) {\r
7070           SetFocus(buttonDesc[0].hwnd);\r
7071         } else {\r
7072           SetFocus(hwndMain);\r
7073         }\r
7074       }\r
7075       return 0;\r
7076     case '\023': /* Ctrl+S */\r
7077       sendNextChar = TRUE;\r
7078       return 0;\r
7079     case '\021': /* Ctrl+Q */\r
7080       quoteNextChar = TRUE;\r
7081       return 0;\r
7082     JAWS_REPLAY\r
7083     default:\r
7084       break;\r
7085     }\r
7086     break;\r
7087   case WM_KEYDOWN:\r
7088     switch (wParam) {\r
7089     case VK_UP:\r
7090       GetWindowText(hwnd, buf, MSG_SIZ);\r
7091       p = PrevInHistory(buf);\r
7092       if (p != NULL) {\r
7093         SetWindowText(hwnd, p);\r
7094         sel.cpMin = 999999;\r
7095         sel.cpMax = 999999;\r
7096         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7097         return 0;\r
7098       }\r
7099       break;\r
7100     case VK_DOWN:\r
7101       p = NextInHistory();\r
7102       if (p != NULL) {\r
7103         SetWindowText(hwnd, p);\r
7104         sel.cpMin = 999999;\r
7105         sel.cpMax = 999999;\r
7106         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7107         return 0;\r
7108       }\r
7109       break;\r
7110     case VK_HOME:\r
7111     case VK_END:\r
7112       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7113       /* fall thru */\r
7114     case VK_PRIOR:\r
7115     case VK_NEXT:\r
7116       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7117       return 0;\r
7118     }\r
7119     break;\r
7120   case WM_MBUTTONDOWN:\r
7121     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7122       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7123     break;\r
7124   case WM_RBUTTONUP:\r
7125     if (GetKeyState(VK_SHIFT) & ~1) {\r
7126       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7127         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7128     } else {\r
7129       POINT pt;\r
7130       HMENU hmenu;\r
7131       hmenu = LoadMenu(hInst, "InputMenu");\r
7132       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7133       if (sel.cpMin == sel.cpMax) {\r
7134         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7135         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7136       }\r
7137       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7138         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7139       }\r
7140       pt.x = LOWORD(lParam);\r
7141       pt.y = HIWORD(lParam);\r
7142       MenuPopup(hwnd, pt, hmenu, -1);\r
7143     }\r
7144     return 0;\r
7145   case WM_COMMAND:\r
7146     switch (LOWORD(wParam)) { \r
7147     case IDM_Undo:\r
7148       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7149       return 0;\r
7150     case IDM_SelectAll:\r
7151       sel.cpMin = 0;\r
7152       sel.cpMax = -1; /*999999?*/\r
7153       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7154       return 0;\r
7155     case IDM_Cut:\r
7156       SendMessage(hwnd, WM_CUT, 0, 0);\r
7157       return 0;\r
7158     case IDM_Paste:\r
7159       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7160       return 0;\r
7161     case IDM_Copy:\r
7162       SendMessage(hwnd, WM_COPY, 0, 0);\r
7163       return 0;\r
7164     }\r
7165     break;\r
7166   }\r
7167   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7168 }\r
7169 \r
7170 #define CO_MAX  100000\r
7171 #define CO_TRIM   1000\r
7172 \r
7173 LRESULT CALLBACK\r
7174 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7175 {\r
7176   static SnapData sd;\r
7177   HWND hText, hInput;\r
7178   RECT rect;\r
7179   static int sizeX, sizeY;\r
7180   int newSizeX, newSizeY;\r
7181   MINMAXINFO *mmi;\r
7182   WORD wMask;\r
7183 \r
7184   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7185   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7186 \r
7187   switch (message) {\r
7188   case WM_NOTIFY:\r
7189     if (((NMHDR*)lParam)->code == EN_LINK)\r
7190     {\r
7191       ENLINK *pLink = (ENLINK*)lParam;\r
7192       if (pLink->msg == WM_LBUTTONUP)\r
7193       {\r
7194         TEXTRANGE tr;\r
7195 \r
7196         tr.chrg = pLink->chrg;\r
7197         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7198         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7199         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7200         free(tr.lpstrText);\r
7201       }\r
7202     }\r
7203     break;\r
7204   case WM_INITDIALOG: /* message: initialize dialog box */\r
7205     hwndConsole = hDlg;\r
7206     SetFocus(hInput);\r
7207     consoleTextWindowProc = (WNDPROC)\r
7208       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7209     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7210     consoleInputWindowProc = (WNDPROC)\r
7211       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7212     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7213     Colorize(ColorNormal, TRUE);\r
7214     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7215     ChangedConsoleFont();\r
7216     GetClientRect(hDlg, &rect);\r
7217     sizeX = rect.right;\r
7218     sizeY = rect.bottom;\r
7219     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7220         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7221       WINDOWPLACEMENT wp;\r
7222       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7223       wp.length = sizeof(WINDOWPLACEMENT);\r
7224       wp.flags = 0;\r
7225       wp.showCmd = SW_SHOW;\r
7226       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7227       wp.rcNormalPosition.left = wpConsole.x;\r
7228       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7229       wp.rcNormalPosition.top = wpConsole.y;\r
7230       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7231       SetWindowPlacement(hDlg, &wp);\r
7232     }\r
7233 \r
7234    // [HGM] Chessknight's change 2004-07-13\r
7235    else { /* Determine Defaults */\r
7236        WINDOWPLACEMENT wp;\r
7237        wpConsole.x = wpMain.width + 1;\r
7238        wpConsole.y = wpMain.y;\r
7239        wpConsole.width = screenWidth -  wpMain.width;\r
7240        wpConsole.height = wpMain.height;\r
7241        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7242        wp.length = sizeof(WINDOWPLACEMENT);\r
7243        wp.flags = 0;\r
7244        wp.showCmd = SW_SHOW;\r
7245        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7246        wp.rcNormalPosition.left = wpConsole.x;\r
7247        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7248        wp.rcNormalPosition.top = wpConsole.y;\r
7249        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7250        SetWindowPlacement(hDlg, &wp);\r
7251     }\r
7252 \r
7253    // Allow hText to highlight URLs and send notifications on them\r
7254    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7255    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7256    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7257    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7258 \r
7259     return FALSE;\r
7260 \r
7261   case WM_SETFOCUS:\r
7262     SetFocus(hInput);\r
7263     return 0;\r
7264 \r
7265   case WM_CLOSE:\r
7266     ExitEvent(0);\r
7267     /* not reached */\r
7268     break;\r
7269 \r
7270   case WM_SIZE:\r
7271     if (IsIconic(hDlg)) break;\r
7272     newSizeX = LOWORD(lParam);\r
7273     newSizeY = HIWORD(lParam);\r
7274     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7275       RECT rectText, rectInput;\r
7276       POINT pt;\r
7277       int newTextHeight, newTextWidth;\r
7278       GetWindowRect(hText, &rectText);\r
7279       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7280       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7281       if (newTextHeight < 0) {\r
7282         newSizeY += -newTextHeight;\r
7283         newTextHeight = 0;\r
7284       }\r
7285       SetWindowPos(hText, NULL, 0, 0,\r
7286         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7287       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7288       pt.x = rectInput.left;\r
7289       pt.y = rectInput.top + newSizeY - sizeY;\r
7290       ScreenToClient(hDlg, &pt);\r
7291       SetWindowPos(hInput, NULL, \r
7292         pt.x, pt.y, /* needs client coords */   \r
7293         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7294         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7295     }\r
7296     sizeX = newSizeX;\r
7297     sizeY = newSizeY;\r
7298     break;\r
7299 \r
7300   case WM_GETMINMAXINFO:\r
7301     /* Prevent resizing window too small */\r
7302     mmi = (MINMAXINFO *) lParam;\r
7303     mmi->ptMinTrackSize.x = 100;\r
7304     mmi->ptMinTrackSize.y = 100;\r
7305     break;\r
7306 \r
7307   /* [AS] Snapping */\r
7308   case WM_ENTERSIZEMOVE:\r
7309     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7310 \r
7311   case WM_SIZING:\r
7312     return OnSizing( &sd, hDlg, wParam, lParam );\r
7313 \r
7314   case WM_MOVING:\r
7315     return OnMoving( &sd, hDlg, wParam, lParam );\r
7316 \r
7317   case WM_EXITSIZEMOVE:\r
7318         UpdateICSWidth(hText);\r
7319     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7320   }\r
7321 \r
7322   return DefWindowProc(hDlg, message, wParam, lParam);\r
7323 }\r
7324 \r
7325 \r
7326 VOID\r
7327 ConsoleCreate()\r
7328 {\r
7329   HWND hCons;\r
7330   if (hwndConsole) return;\r
7331   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7332   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7333 }\r
7334 \r
7335 \r
7336 VOID\r
7337 ConsoleOutput(char* data, int length, int forceVisible)\r
7338 {\r
7339   HWND hText;\r
7340   int trim, exlen;\r
7341   char *p, *q;\r
7342   char buf[CO_MAX+1];\r
7343   POINT pEnd;\r
7344   RECT rect;\r
7345   static int delayLF = 0;\r
7346   CHARRANGE savesel, sel;\r
7347 \r
7348   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7349   p = data;\r
7350   q = buf;\r
7351   if (delayLF) {\r
7352     *q++ = '\r';\r
7353     *q++ = '\n';\r
7354     delayLF = 0;\r
7355   }\r
7356   while (length--) {\r
7357     if (*p == '\n') {\r
7358       if (*++p) {\r
7359         *q++ = '\r';\r
7360         *q++ = '\n';\r
7361       } else {\r
7362         delayLF = 1;\r
7363       }\r
7364     } else if (*p == '\007') {\r
7365        MyPlaySound(&sounds[(int)SoundBell]);\r
7366        p++;\r
7367     } else {\r
7368       *q++ = *p++;\r
7369     }\r
7370   }\r
7371   *q = NULLCHAR;\r
7372   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7373   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7374   /* Save current selection */\r
7375   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7376   exlen = GetWindowTextLength(hText);\r
7377   /* Find out whether current end of text is visible */\r
7378   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7379   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7380   /* Trim existing text if it's too long */\r
7381   if (exlen + (q - buf) > CO_MAX) {\r
7382     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7383     sel.cpMin = 0;\r
7384     sel.cpMax = trim;\r
7385     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7386     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7387     exlen -= trim;\r
7388     savesel.cpMin -= trim;\r
7389     savesel.cpMax -= trim;\r
7390     if (exlen < 0) exlen = 0;\r
7391     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7392     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7393   }\r
7394   /* Append the new text */\r
7395   sel.cpMin = exlen;\r
7396   sel.cpMax = exlen;\r
7397   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7398   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7399   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7400   if (forceVisible || exlen == 0 ||\r
7401       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7402        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7403     /* Scroll to make new end of text visible if old end of text\r
7404        was visible or new text is an echo of user typein */\r
7405     sel.cpMin = 9999999;\r
7406     sel.cpMax = 9999999;\r
7407     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7408     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7409     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7410     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7411   }\r
7412   if (savesel.cpMax == exlen || forceVisible) {\r
7413     /* Move insert point to new end of text if it was at the old\r
7414        end of text or if the new text is an echo of user typein */\r
7415     sel.cpMin = 9999999;\r
7416     sel.cpMax = 9999999;\r
7417     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7418   } else {\r
7419     /* Restore previous selection */\r
7420     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7421   }\r
7422   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7423 }\r
7424 \r
7425 /*---------*/\r
7426 \r
7427 \r
7428 void\r
7429 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7430 {\r
7431   char buf[100];\r
7432   char *str;\r
7433   COLORREF oldFg, oldBg;\r
7434   HFONT oldFont;\r
7435   RECT rect;\r
7436 \r
7437   if(copyNumber > 1)
7438     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7439 \r
7440   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7441   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7442   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7443 \r
7444   rect.left = x;\r
7445   rect.right = x + squareSize;\r
7446   rect.top  = y;\r
7447   rect.bottom = y + squareSize;\r
7448   str = buf;\r
7449 \r
7450   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7451                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7452              y, ETO_CLIPPED|ETO_OPAQUE,\r
7453              &rect, str, strlen(str), NULL);\r
7454 \r
7455   (void) SetTextColor(hdc, oldFg);\r
7456   (void) SetBkColor(hdc, oldBg);\r
7457   (void) SelectObject(hdc, oldFont);\r
7458 }\r
7459 \r
7460 void\r
7461 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7462               RECT *rect, char *color, char *flagFell)\r
7463 {\r
7464   char buf[100];\r
7465   char *str;\r
7466   COLORREF oldFg, oldBg;\r
7467   HFONT oldFont;\r
7468 \r
7469   if (appData.clockMode) {\r
7470     if (tinyLayout)\r
7471       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7472     else\r
7473       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7474     str = buf;\r
7475   } else {\r
7476     str = color;\r
7477   }\r
7478 \r
7479   if (highlight) {\r
7480     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7481     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7482   } else {\r
7483     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7484     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7485   }\r
7486   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7487 \r
7488   JAWS_SILENCE\r
7489 \r
7490   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7491              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7492              rect, str, strlen(str), NULL);\r
7493   if(logoHeight > 0 && appData.clockMode) {\r
7494       RECT r;\r
7495       str += strlen(color)+2;\r
7496       r.top = rect->top + logoHeight/2;\r
7497       r.left = rect->left;\r
7498       r.right = rect->right;\r
7499       r.bottom = rect->bottom;\r
7500       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7501                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7502                  &r, str, strlen(str), NULL);\r
7503   }\r
7504   (void) SetTextColor(hdc, oldFg);\r
7505   (void) SetBkColor(hdc, oldBg);\r
7506   (void) SelectObject(hdc, oldFont);\r
7507 }\r
7508 \r
7509 \r
7510 int\r
7511 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7512            OVERLAPPED *ovl)\r
7513 {\r
7514   int ok, err;\r
7515 \r
7516   /* [AS]  */\r
7517   if( count <= 0 ) {\r
7518     if (appData.debugMode) {\r
7519       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7520     }\r
7521 \r
7522     return ERROR_INVALID_USER_BUFFER;\r
7523   }\r
7524 \r
7525   ResetEvent(ovl->hEvent);\r
7526   ovl->Offset = ovl->OffsetHigh = 0;\r
7527   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7528   if (ok) {\r
7529     err = NO_ERROR;\r
7530   } else {\r
7531     err = GetLastError();\r
7532     if (err == ERROR_IO_PENDING) {\r
7533       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7534       if (ok)\r
7535         err = NO_ERROR;\r
7536       else\r
7537         err = GetLastError();\r
7538     }\r
7539   }\r
7540   return err;\r
7541 }\r
7542 \r
7543 int\r
7544 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7545             OVERLAPPED *ovl)\r
7546 {\r
7547   int ok, err;\r
7548 \r
7549   ResetEvent(ovl->hEvent);\r
7550   ovl->Offset = ovl->OffsetHigh = 0;\r
7551   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7552   if (ok) {\r
7553     err = NO_ERROR;\r
7554   } else {\r
7555     err = GetLastError();\r
7556     if (err == ERROR_IO_PENDING) {\r
7557       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7558       if (ok)\r
7559         err = NO_ERROR;\r
7560       else\r
7561         err = GetLastError();\r
7562     }\r
7563   }\r
7564   return err;\r
7565 }\r
7566 \r
7567 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7568 void CheckForInputBufferFull( InputSource * is )\r
7569 {\r
7570     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7571         /* Look for end of line */\r
7572         char * p = is->buf;\r
7573         \r
7574         while( p < is->next && *p != '\n' ) {\r
7575             p++;\r
7576         }\r
7577 \r
7578         if( p >= is->next ) {\r
7579             if (appData.debugMode) {\r
7580                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7581             }\r
7582 \r
7583             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7584             is->count = (DWORD) -1;\r
7585             is->next = is->buf;\r
7586         }\r
7587     }\r
7588 }\r
7589 \r
7590 DWORD\r
7591 InputThread(LPVOID arg)\r
7592 {\r
7593   InputSource *is;\r
7594   OVERLAPPED ovl;\r
7595 \r
7596   is = (InputSource *) arg;\r
7597   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7598   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7599   while (is->hThread != NULL) {\r
7600     is->error = DoReadFile(is->hFile, is->next,\r
7601                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7602                            &is->count, &ovl);\r
7603     if (is->error == NO_ERROR) {\r
7604       is->next += is->count;\r
7605     } else {\r
7606       if (is->error == ERROR_BROKEN_PIPE) {\r
7607         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7608         is->count = 0;\r
7609       } else {\r
7610         is->count = (DWORD) -1;\r
7611         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7612         break; \r
7613       }\r
7614     }\r
7615 \r
7616     CheckForInputBufferFull( is );\r
7617 \r
7618     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7619 \r
7620     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7621 \r
7622     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7623   }\r
7624 \r
7625   CloseHandle(ovl.hEvent);\r
7626   CloseHandle(is->hFile);\r
7627 \r
7628   if (appData.debugMode) {\r
7629     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7630   }\r
7631 \r
7632   return 0;\r
7633 }\r
7634 \r
7635 \r
7636 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7637 DWORD\r
7638 NonOvlInputThread(LPVOID arg)\r
7639 {\r
7640   InputSource *is;\r
7641   char *p, *q;\r
7642   int i;\r
7643   char prev;\r
7644 \r
7645   is = (InputSource *) arg;\r
7646   while (is->hThread != NULL) {\r
7647     is->error = ReadFile(is->hFile, is->next,\r
7648                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7649                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7650     if (is->error == NO_ERROR) {\r
7651       /* Change CRLF to LF */\r
7652       if (is->next > is->buf) {\r
7653         p = is->next - 1;\r
7654         i = is->count + 1;\r
7655       } else {\r
7656         p = is->next;\r
7657         i = is->count;\r
7658       }\r
7659       q = p;\r
7660       prev = NULLCHAR;\r
7661       while (i > 0) {\r
7662         if (prev == '\r' && *p == '\n') {\r
7663           *(q-1) = '\n';\r
7664           is->count--;\r
7665         } else { \r
7666           *q++ = *p;\r
7667         }\r
7668         prev = *p++;\r
7669         i--;\r
7670       }\r
7671       *q = NULLCHAR;\r
7672       is->next = q;\r
7673     } else {\r
7674       if (is->error == ERROR_BROKEN_PIPE) {\r
7675         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7676         is->count = 0; \r
7677       } else {\r
7678         is->count = (DWORD) -1;\r
7679       }\r
7680     }\r
7681 \r
7682     CheckForInputBufferFull( is );\r
7683 \r
7684     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7685 \r
7686     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7687 \r
7688     if (is->count < 0) break;  /* Quit on error */\r
7689   }\r
7690   CloseHandle(is->hFile);\r
7691   return 0;\r
7692 }\r
7693 \r
7694 DWORD\r
7695 SocketInputThread(LPVOID arg)\r
7696 {\r
7697   InputSource *is;\r
7698 \r
7699   is = (InputSource *) arg;\r
7700   while (is->hThread != NULL) {\r
7701     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7702     if ((int)is->count == SOCKET_ERROR) {\r
7703       is->count = (DWORD) -1;\r
7704       is->error = WSAGetLastError();\r
7705     } else {\r
7706       is->error = NO_ERROR;\r
7707       is->next += is->count;\r
7708       if (is->count == 0 && is->second == is) {\r
7709         /* End of file on stderr; quit with no message */\r
7710         break;\r
7711       }\r
7712     }\r
7713     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7714 \r
7715     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7716 \r
7717     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7718   }\r
7719   return 0;\r
7720 }\r
7721 \r
7722 VOID\r
7723 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7724 {\r
7725   InputSource *is;\r
7726 \r
7727   is = (InputSource *) lParam;\r
7728   if (is->lineByLine) {\r
7729     /* Feed in lines one by one */\r
7730     char *p = is->buf;\r
7731     char *q = p;\r
7732     while (q < is->next) {\r
7733       if (*q++ == '\n') {\r
7734         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7735         p = q;\r
7736       }\r
7737     }\r
7738     \r
7739     /* Move any partial line to the start of the buffer */\r
7740     q = is->buf;\r
7741     while (p < is->next) {\r
7742       *q++ = *p++;\r
7743     }\r
7744     is->next = q;\r
7745 \r
7746     if (is->error != NO_ERROR || is->count == 0) {\r
7747       /* Notify backend of the error.  Note: If there was a partial\r
7748          line at the end, it is not flushed through. */\r
7749       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7750     }\r
7751   } else {\r
7752     /* Feed in the whole chunk of input at once */\r
7753     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7754     is->next = is->buf;\r
7755   }\r
7756 }\r
7757 \r
7758 /*---------------------------------------------------------------------------*\\r
7759  *\r
7760  *  Menu enables. Used when setting various modes.\r
7761  *\r
7762 \*---------------------------------------------------------------------------*/\r
7763 \r
7764 typedef struct {\r
7765   int item;\r
7766   int flags;\r
7767 } Enables;\r
7768 \r
7769 VOID\r
7770 GreyRevert(Boolean grey)\r
7771 { // [HGM] vari: for retracting variations in local mode\r
7772   HMENU hmenu = GetMenu(hwndMain);\r
7773   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7774   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7775 }\r
7776 \r
7777 VOID\r
7778 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7779 {\r
7780   while (enab->item > 0) {\r
7781     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7782     enab++;\r
7783   }\r
7784 }\r
7785 \r
7786 Enables gnuEnables[] = {\r
7787   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7800   { -1, -1 }\r
7801 };\r
7802 \r
7803 Enables icsEnables[] = {\r
7804   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7808   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7809   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7810   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7811   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7812   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7813   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7818   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7821   { -1, -1 }\r
7822 };\r
7823 \r
7824 #if ZIPPY\r
7825 Enables zippyEnables[] = {\r
7826   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7827   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7828   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7829   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7830   { -1, -1 }\r
7831 };\r
7832 #endif\r
7833 \r
7834 Enables ncpEnables[] = {\r
7835   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7844   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7845   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7856   { -1, -1 }\r
7857 };\r
7858 \r
7859 Enables trainingOnEnables[] = {\r
7860   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7867   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7868   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7869   { -1, -1 }\r
7870 };\r
7871 \r
7872 Enables trainingOffEnables[] = {\r
7873   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7877   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7881   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7882   { -1, -1 }\r
7883 };\r
7884 \r
7885 /* These modify either ncpEnables or gnuEnables */\r
7886 Enables cmailEnables[] = {\r
7887   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7888   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7889   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7890   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7892   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7894   { -1, -1 }\r
7895 };\r
7896 \r
7897 Enables machineThinkingEnables[] = {\r
7898   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7913   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7914   { -1, -1 }\r
7915 };\r
7916 \r
7917 Enables userThinkingEnables[] = {\r
7918   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7928   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7929   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7930   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7931   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7932   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7934   { -1, -1 }\r
7935 };\r
7936 \r
7937 /*---------------------------------------------------------------------------*\\r
7938  *\r
7939  *  Front-end interface functions exported by XBoard.\r
7940  *  Functions appear in same order as prototypes in frontend.h.\r
7941  * \r
7942 \*---------------------------------------------------------------------------*/\r
7943 VOID\r
7944 ModeHighlight()\r
7945 {\r
7946   static UINT prevChecked = 0;\r
7947   static int prevPausing = 0;\r
7948   UINT nowChecked;\r
7949 \r
7950   if (pausing != prevPausing) {\r
7951     prevPausing = pausing;\r
7952     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7953                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7954     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7955   }\r
7956 \r
7957   switch (gameMode) {\r
7958   case BeginningOfGame:\r
7959     if (appData.icsActive)\r
7960       nowChecked = IDM_IcsClient;\r
7961     else if (appData.noChessProgram)\r
7962       nowChecked = IDM_EditGame;\r
7963     else\r
7964       nowChecked = IDM_MachineBlack;\r
7965     break;\r
7966   case MachinePlaysBlack:\r
7967     nowChecked = IDM_MachineBlack;\r
7968     break;\r
7969   case MachinePlaysWhite:\r
7970     nowChecked = IDM_MachineWhite;\r
7971     break;\r
7972   case TwoMachinesPlay:\r
7973     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7974     break;\r
7975   case AnalyzeMode:\r
7976     nowChecked = IDM_AnalysisMode;\r
7977     break;\r
7978   case AnalyzeFile:\r
7979     nowChecked = IDM_AnalyzeFile;\r
7980     break;\r
7981   case EditGame:\r
7982     nowChecked = IDM_EditGame;\r
7983     break;\r
7984   case PlayFromGameFile:\r
7985     nowChecked = IDM_LoadGame;\r
7986     break;\r
7987   case EditPosition:\r
7988     nowChecked = IDM_EditPosition;\r
7989     break;\r
7990   case Training:\r
7991     nowChecked = IDM_Training;\r
7992     break;\r
7993   case IcsPlayingWhite:\r
7994   case IcsPlayingBlack:\r
7995   case IcsObserving:\r
7996   case IcsIdle:\r
7997     nowChecked = IDM_IcsClient;\r
7998     break;\r
7999   default:\r
8000   case EndOfGame:\r
8001     nowChecked = 0;\r
8002     break;\r
8003   }\r
8004   if (prevChecked != 0)\r
8005     (void) CheckMenuItem(GetMenu(hwndMain),\r
8006                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8007   if (nowChecked != 0)\r
8008     (void) CheckMenuItem(GetMenu(hwndMain),\r
8009                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8010 \r
8011   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8012     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8013                           MF_BYCOMMAND|MF_ENABLED);\r
8014   } else {\r
8015     (void) EnableMenuItem(GetMenu(hwndMain), \r
8016                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8017   }\r
8018 \r
8019   prevChecked = nowChecked;\r
8020 \r
8021   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8022   if (appData.icsActive) {\r
8023        if (appData.icsEngineAnalyze) {\r
8024                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8025                        MF_BYCOMMAND|MF_CHECKED);\r
8026        } else {\r
8027                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8028                        MF_BYCOMMAND|MF_UNCHECKED);\r
8029        }\r
8030   }\r
8031   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8032 }\r
8033 \r
8034 VOID\r
8035 SetICSMode()\r
8036 {\r
8037   HMENU hmenu = GetMenu(hwndMain);\r
8038   SetMenuEnables(hmenu, icsEnables);\r
8039   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8040     MF_BYPOSITION|MF_ENABLED);\r
8041 #if ZIPPY\r
8042   if (appData.zippyPlay) {\r
8043     SetMenuEnables(hmenu, zippyEnables);\r
8044     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8045          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8046           MF_BYCOMMAND|MF_ENABLED);\r
8047   }\r
8048 #endif\r
8049 }\r
8050 \r
8051 VOID\r
8052 SetGNUMode()\r
8053 {\r
8054   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8055 }\r
8056 \r
8057 VOID\r
8058 SetNCPMode()\r
8059 {\r
8060   HMENU hmenu = GetMenu(hwndMain);\r
8061   SetMenuEnables(hmenu, ncpEnables);\r
8062   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8063     MF_BYPOSITION|MF_GRAYED);\r
8064     DrawMenuBar(hwndMain);\r
8065 }\r
8066 \r
8067 VOID\r
8068 SetCmailMode()\r
8069 {\r
8070   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8071 }\r
8072 \r
8073 VOID \r
8074 SetTrainingModeOn()\r
8075 {\r
8076   int i;\r
8077   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8078   for (i = 0; i < N_BUTTONS; i++) {\r
8079     if (buttonDesc[i].hwnd != NULL)\r
8080       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8081   }\r
8082   CommentPopDown();\r
8083 }\r
8084 \r
8085 VOID SetTrainingModeOff()\r
8086 {\r
8087   int i;\r
8088   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8089   for (i = 0; i < N_BUTTONS; i++) {\r
8090     if (buttonDesc[i].hwnd != NULL)\r
8091       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8092   }\r
8093 }\r
8094 \r
8095 \r
8096 VOID\r
8097 SetUserThinkingEnables()\r
8098 {\r
8099   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8100 }\r
8101 \r
8102 VOID\r
8103 SetMachineThinkingEnables()\r
8104 {\r
8105   HMENU hMenu = GetMenu(hwndMain);\r
8106   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8107 \r
8108   SetMenuEnables(hMenu, machineThinkingEnables);\r
8109 \r
8110   if (gameMode == MachinePlaysBlack) {\r
8111     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8112   } else if (gameMode == MachinePlaysWhite) {\r
8113     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8114   } else if (gameMode == TwoMachinesPlay) {\r
8115     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8116   }\r
8117 }\r
8118 \r
8119 \r
8120 VOID\r
8121 DisplayTitle(char *str)\r
8122 {\r
8123   char title[MSG_SIZ], *host;\r
8124   if (str[0] != NULLCHAR) {\r
8125     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8126   } else if (appData.icsActive) {\r
8127     if (appData.icsCommPort[0] != NULLCHAR)\r
8128       host = "ICS";\r
8129     else \r
8130       host = appData.icsHost;\r
8131       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8132   } else if (appData.noChessProgram) {\r
8133     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8134   } else {\r
8135     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8136     strcat(title, ": ");\r
8137     strcat(title, first.tidy);\r
8138   }\r
8139   SetWindowText(hwndMain, title);\r
8140 }\r
8141 \r
8142 \r
8143 VOID\r
8144 DisplayMessage(char *str1, char *str2)\r
8145 {\r
8146   HDC hdc;\r
8147   HFONT oldFont;\r
8148   int remain = MESSAGE_TEXT_MAX - 1;\r
8149   int len;\r
8150 \r
8151   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8152   messageText[0] = NULLCHAR;\r
8153   if (*str1) {\r
8154     len = strlen(str1);\r
8155     if (len > remain) len = remain;\r
8156     strncpy(messageText, str1, len);\r
8157     messageText[len] = NULLCHAR;\r
8158     remain -= len;\r
8159   }\r
8160   if (*str2 && remain >= 2) {\r
8161     if (*str1) {\r
8162       strcat(messageText, "  ");\r
8163       remain -= 2;\r
8164     }\r
8165     len = strlen(str2);\r
8166     if (len > remain) len = remain;\r
8167     strncat(messageText, str2, len);\r
8168   }\r
8169   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8170 \r
8171   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8172 \r
8173   SAYMACHINEMOVE();\r
8174 \r
8175   hdc = GetDC(hwndMain);\r
8176   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8177   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8178              &messageRect, messageText, strlen(messageText), NULL);\r
8179   (void) SelectObject(hdc, oldFont);\r
8180   (void) ReleaseDC(hwndMain, hdc);\r
8181 }\r
8182 \r
8183 VOID\r
8184 DisplayError(char *str, int error)\r
8185 {\r
8186   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8187   int len;\r
8188 \r
8189   if (error == 0) {\r
8190     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8191   } else {\r
8192     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8193                         NULL, error, LANG_NEUTRAL,\r
8194                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8195     if (len > 0) {\r
8196       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8197     } else {\r
8198       ErrorMap *em = errmap;\r
8199       while (em->err != 0 && em->err != error) em++;\r
8200       if (em->err != 0) {\r
8201         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8202       } else {\r
8203         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8204       }\r
8205     }\r
8206   }\r
8207   \r
8208   ErrorPopUp(_("Error"), buf);\r
8209 }\r
8210 \r
8211 \r
8212 VOID\r
8213 DisplayMoveError(char *str)\r
8214 {\r
8215   fromX = fromY = -1;\r
8216   ClearHighlights();\r
8217   DrawPosition(FALSE, NULL);\r
8218   if (appData.popupMoveErrors) {\r
8219     ErrorPopUp(_("Error"), str);\r
8220   } else {\r
8221     DisplayMessage(str, "");\r
8222     moveErrorMessageUp = TRUE;\r
8223   }\r
8224 }\r
8225 \r
8226 VOID\r
8227 DisplayFatalError(char *str, int error, int exitStatus)\r
8228 {\r
8229   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8230   int len;\r
8231   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8232 \r
8233   if (error != 0) {\r
8234     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8235                         NULL, error, LANG_NEUTRAL,\r
8236                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8237     if (len > 0) {\r
8238       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8239     } else {\r
8240       ErrorMap *em = errmap;\r
8241       while (em->err != 0 && em->err != error) em++;\r
8242       if (em->err != 0) {\r
8243         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8244       } else {\r
8245         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8246       }\r
8247     }\r
8248     str = buf;\r
8249   }\r
8250   if (appData.debugMode) {\r
8251     fprintf(debugFP, "%s: %s\n", label, str);\r
8252   }\r
8253   if (appData.popupExitMessage) {\r
8254     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8255                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8256   }\r
8257   ExitEvent(exitStatus);\r
8258 }\r
8259 \r
8260 \r
8261 VOID\r
8262 DisplayInformation(char *str)\r
8263 {\r
8264   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8265 }\r
8266 \r
8267 \r
8268 VOID\r
8269 DisplayNote(char *str)\r
8270 {\r
8271   ErrorPopUp(_("Note"), str);\r
8272 }\r
8273 \r
8274 \r
8275 typedef struct {\r
8276   char *title, *question, *replyPrefix;\r
8277   ProcRef pr;\r
8278 } QuestionParams;\r
8279 \r
8280 LRESULT CALLBACK\r
8281 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8282 {\r
8283   static QuestionParams *qp;\r
8284   char reply[MSG_SIZ];\r
8285   int len, err;\r
8286 \r
8287   switch (message) {\r
8288   case WM_INITDIALOG:\r
8289     qp = (QuestionParams *) lParam;\r
8290     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8291     Translate(hDlg, DLG_Question);\r
8292     SetWindowText(hDlg, qp->title);\r
8293     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8294     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8295     return FALSE;\r
8296 \r
8297   case WM_COMMAND:\r
8298     switch (LOWORD(wParam)) {\r
8299     case IDOK:\r
8300       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8301       if (*reply) strcat(reply, " ");\r
8302       len = strlen(reply);\r
8303       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8304       strcat(reply, "\n");\r
8305       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8306       EndDialog(hDlg, TRUE);\r
8307       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8308       return TRUE;\r
8309     case IDCANCEL:\r
8310       EndDialog(hDlg, FALSE);\r
8311       return TRUE;\r
8312     default:\r
8313       break;\r
8314     }\r
8315     break;\r
8316   }\r
8317   return FALSE;\r
8318 }\r
8319 \r
8320 VOID\r
8321 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8322 {\r
8323     QuestionParams qp;\r
8324     FARPROC lpProc;\r
8325     \r
8326     qp.title = title;\r
8327     qp.question = question;\r
8328     qp.replyPrefix = replyPrefix;\r
8329     qp.pr = pr;\r
8330     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8331     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8332       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8333     FreeProcInstance(lpProc);\r
8334 }\r
8335 \r
8336 /* [AS] Pick FRC position */\r
8337 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8338 {\r
8339     static int * lpIndexFRC;\r
8340     BOOL index_is_ok;\r
8341     char buf[16];\r
8342 \r
8343     switch( message )\r
8344     {\r
8345     case WM_INITDIALOG:\r
8346         lpIndexFRC = (int *) lParam;\r
8347 \r
8348         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8349         Translate(hDlg, DLG_NewGameFRC);\r
8350 \r
8351         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8352         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8353         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8354         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8355 \r
8356         break;\r
8357 \r
8358     case WM_COMMAND:\r
8359         switch( LOWORD(wParam) ) {\r
8360         case IDOK:\r
8361             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8362             EndDialog( hDlg, 0 );\r
8363             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8364             return TRUE;\r
8365         case IDCANCEL:\r
8366             EndDialog( hDlg, 1 );   \r
8367             return TRUE;\r
8368         case IDC_NFG_Edit:\r
8369             if( HIWORD(wParam) == EN_CHANGE ) {\r
8370                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8371 \r
8372                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8373             }\r
8374             return TRUE;\r
8375         case IDC_NFG_Random:\r
8376           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8377             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8378             return TRUE;\r
8379         }\r
8380 \r
8381         break;\r
8382     }\r
8383 \r
8384     return FALSE;\r
8385 }\r
8386 \r
8387 int NewGameFRC()\r
8388 {\r
8389     int result;\r
8390     int index = appData.defaultFrcPosition;\r
8391     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8392 \r
8393     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8394 \r
8395     if( result == 0 ) {\r
8396         appData.defaultFrcPosition = index;\r
8397     }\r
8398 \r
8399     return result;\r
8400 }\r
8401 \r
8402 /* [AS] Game list options. Refactored by HGM */\r
8403 \r
8404 HWND gameListOptionsDialog;\r
8405 \r
8406 // low-level front-end: clear text edit / list widget\r
8407 void\r
8408 GLT_ClearList()\r
8409 {\r
8410     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8411 }\r
8412 \r
8413 // low-level front-end: clear text edit / list widget\r
8414 void\r
8415 GLT_DeSelectList()\r
8416 {\r
8417     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8418 }\r
8419 \r
8420 // low-level front-end: append line to text edit / list widget\r
8421 void\r
8422 GLT_AddToList( char *name )\r
8423 {\r
8424     if( name != 0 ) {\r
8425             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8426     }\r
8427 }\r
8428 \r
8429 // low-level front-end: get line from text edit / list widget\r
8430 Boolean\r
8431 GLT_GetFromList( int index, char *name )\r
8432 {\r
8433     if( name != 0 ) {\r
8434             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8435                 return TRUE;\r
8436     }\r
8437     return FALSE;\r
8438 }\r
8439 \r
8440 void GLT_MoveSelection( HWND hDlg, int delta )\r
8441 {\r
8442     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8443     int idx2 = idx1 + delta;\r
8444     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8445 \r
8446     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8447         char buf[128];\r
8448 \r
8449         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8450         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8451         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8452         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8453     }\r
8454 }\r
8455 \r
8456 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8457 {\r
8458     switch( message )\r
8459     {\r
8460     case WM_INITDIALOG:\r
8461         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8462         \r
8463         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8464         Translate(hDlg, DLG_GameListOptions);\r
8465 \r
8466         /* Initialize list */\r
8467         GLT_TagsToList( lpUserGLT );\r
8468 \r
8469         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8470 \r
8471         break;\r
8472 \r
8473     case WM_COMMAND:\r
8474         switch( LOWORD(wParam) ) {\r
8475         case IDOK:\r
8476             GLT_ParseList();\r
8477             EndDialog( hDlg, 0 );\r
8478             return TRUE;\r
8479         case IDCANCEL:\r
8480             EndDialog( hDlg, 1 );\r
8481             return TRUE;\r
8482 \r
8483         case IDC_GLT_Default:\r
8484             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8485             return TRUE;\r
8486 \r
8487         case IDC_GLT_Restore:\r
8488             GLT_TagsToList( appData.gameListTags );\r
8489             return TRUE;\r
8490 \r
8491         case IDC_GLT_Up:\r
8492             GLT_MoveSelection( hDlg, -1 );\r
8493             return TRUE;\r
8494 \r
8495         case IDC_GLT_Down:\r
8496             GLT_MoveSelection( hDlg, +1 );\r
8497             return TRUE;\r
8498         }\r
8499 \r
8500         break;\r
8501     }\r
8502 \r
8503     return FALSE;\r
8504 }\r
8505 \r
8506 int GameListOptions()\r
8507 {\r
8508     int result;\r
8509     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8510 \r
8511       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8512 \r
8513     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8514 \r
8515     if( result == 0 ) {\r
8516         /* [AS] Memory leak here! */\r
8517         appData.gameListTags = strdup( lpUserGLT ); \r
8518     }\r
8519 \r
8520     return result;\r
8521 }\r
8522 \r
8523 VOID\r
8524 DisplayIcsInteractionTitle(char *str)\r
8525 {\r
8526   char consoleTitle[MSG_SIZ];\r
8527 \r
8528     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8529   SetWindowText(hwndConsole, consoleTitle);\r
8530 }\r
8531 \r
8532 void\r
8533 DrawPosition(int fullRedraw, Board board)\r
8534 {\r
8535   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8536 }\r
8537 \r
8538 void NotifyFrontendLogin()\r
8539 {\r
8540         if (hwndConsole)\r
8541                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8542 }\r
8543 \r
8544 VOID\r
8545 ResetFrontEnd()\r
8546 {\r
8547   fromX = fromY = -1;\r
8548   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8549     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8550     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8551     dragInfo.lastpos = dragInfo.pos;\r
8552     dragInfo.start.x = dragInfo.start.y = -1;\r
8553     dragInfo.from = dragInfo.start;\r
8554     ReleaseCapture();\r
8555     DrawPosition(TRUE, NULL);\r
8556   }\r
8557   TagsPopDown();\r
8558 }\r
8559 \r
8560 \r
8561 VOID\r
8562 CommentPopUp(char *title, char *str)\r
8563 {\r
8564   HWND hwnd = GetActiveWindow();\r
8565   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8566   SAY(str);\r
8567   SetActiveWindow(hwnd);\r
8568 }\r
8569 \r
8570 VOID\r
8571 CommentPopDown(void)\r
8572 {\r
8573   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8574   if (commentDialog) {\r
8575     ShowWindow(commentDialog, SW_HIDE);\r
8576   }\r
8577   commentUp = FALSE;\r
8578 }\r
8579 \r
8580 VOID\r
8581 EditCommentPopUp(int index, char *title, char *str)\r
8582 {\r
8583   EitherCommentPopUp(index, title, str, TRUE);\r
8584 }\r
8585 \r
8586 \r
8587 VOID\r
8588 RingBell()\r
8589 {\r
8590   MyPlaySound(&sounds[(int)SoundMove]);\r
8591 }\r
8592 \r
8593 VOID PlayIcsWinSound()\r
8594 {\r
8595   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8596 }\r
8597 \r
8598 VOID PlayIcsLossSound()\r
8599 {\r
8600   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8601 }\r
8602 \r
8603 VOID PlayIcsDrawSound()\r
8604 {\r
8605   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8606 }\r
8607 \r
8608 VOID PlayIcsUnfinishedSound()\r
8609 {\r
8610   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8611 }\r
8612 \r
8613 VOID\r
8614 PlayAlarmSound()\r
8615 {\r
8616   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8617 }\r
8618 \r
8619 \r
8620 VOID\r
8621 EchoOn()\r
8622 {\r
8623   HWND hInput;\r
8624   consoleEcho = TRUE;\r
8625   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8626   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8627   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8628 }\r
8629 \r
8630 \r
8631 VOID\r
8632 EchoOff()\r
8633 {\r
8634   CHARFORMAT cf;\r
8635   HWND hInput;\r
8636   consoleEcho = FALSE;\r
8637   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8638   /* This works OK: set text and background both to the same color */\r
8639   cf = consoleCF;\r
8640   cf.crTextColor = COLOR_ECHOOFF;\r
8641   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8642   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8643 }\r
8644 \r
8645 /* No Raw()...? */\r
8646 \r
8647 void Colorize(ColorClass cc, int continuation)\r
8648 {\r
8649   currentColorClass = cc;\r
8650   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8651   consoleCF.crTextColor = textAttribs[cc].color;\r
8652   consoleCF.dwEffects = textAttribs[cc].effects;\r
8653   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8654 }\r
8655 \r
8656 char *\r
8657 UserName()\r
8658 {\r
8659   static char buf[MSG_SIZ];\r
8660   DWORD bufsiz = MSG_SIZ;\r
8661 \r
8662   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8663         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8664   }\r
8665   if (!GetUserName(buf, &bufsiz)) {\r
8666     /*DisplayError("Error getting user name", GetLastError());*/\r
8667     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8668   }\r
8669   return buf;\r
8670 }\r
8671 \r
8672 char *\r
8673 HostName()\r
8674 {\r
8675   static char buf[MSG_SIZ];\r
8676   DWORD bufsiz = MSG_SIZ;\r
8677 \r
8678   if (!GetComputerName(buf, &bufsiz)) {\r
8679     /*DisplayError("Error getting host name", GetLastError());*/\r
8680     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8681   }\r
8682   return buf;\r
8683 }\r
8684 \r
8685 \r
8686 int\r
8687 ClockTimerRunning()\r
8688 {\r
8689   return clockTimerEvent != 0;\r
8690 }\r
8691 \r
8692 int\r
8693 StopClockTimer()\r
8694 {\r
8695   if (clockTimerEvent == 0) return FALSE;\r
8696   KillTimer(hwndMain, clockTimerEvent);\r
8697   clockTimerEvent = 0;\r
8698   return TRUE;\r
8699 }\r
8700 \r
8701 void\r
8702 StartClockTimer(long millisec)\r
8703 {\r
8704   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8705                              (UINT) millisec, NULL);\r
8706 }\r
8707 \r
8708 void\r
8709 DisplayWhiteClock(long timeRemaining, int highlight)\r
8710 {\r
8711   HDC hdc;\r
8712   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8713 \r
8714   if(appData.noGUI) return;\r
8715   hdc = GetDC(hwndMain);\r
8716   if (!IsIconic(hwndMain)) {\r
8717     DisplayAClock(hdc, timeRemaining, highlight, \r
8718                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8719   }\r
8720   if (highlight && iconCurrent == iconBlack) {\r
8721     iconCurrent = iconWhite;\r
8722     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8723     if (IsIconic(hwndMain)) {\r
8724       DrawIcon(hdc, 2, 2, iconCurrent);\r
8725     }\r
8726   }\r
8727   (void) ReleaseDC(hwndMain, hdc);\r
8728   if (hwndConsole)\r
8729     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8730 }\r
8731 \r
8732 void\r
8733 DisplayBlackClock(long timeRemaining, int highlight)\r
8734 {\r
8735   HDC hdc;\r
8736   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8737 \r
8738   if(appData.noGUI) return;\r
8739   hdc = GetDC(hwndMain);\r
8740   if (!IsIconic(hwndMain)) {\r
8741     DisplayAClock(hdc, timeRemaining, highlight, \r
8742                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8743   }\r
8744   if (highlight && iconCurrent == iconWhite) {\r
8745     iconCurrent = iconBlack;\r
8746     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8747     if (IsIconic(hwndMain)) {\r
8748       DrawIcon(hdc, 2, 2, iconCurrent);\r
8749     }\r
8750   }\r
8751   (void) ReleaseDC(hwndMain, hdc);\r
8752   if (hwndConsole)\r
8753     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8754 }\r
8755 \r
8756 \r
8757 int\r
8758 LoadGameTimerRunning()\r
8759 {\r
8760   return loadGameTimerEvent != 0;\r
8761 }\r
8762 \r
8763 int\r
8764 StopLoadGameTimer()\r
8765 {\r
8766   if (loadGameTimerEvent == 0) return FALSE;\r
8767   KillTimer(hwndMain, loadGameTimerEvent);\r
8768   loadGameTimerEvent = 0;\r
8769   return TRUE;\r
8770 }\r
8771 \r
8772 void\r
8773 StartLoadGameTimer(long millisec)\r
8774 {\r
8775   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8776                                 (UINT) millisec, NULL);\r
8777 }\r
8778 \r
8779 void\r
8780 AutoSaveGame()\r
8781 {\r
8782   char *defName;\r
8783   FILE *f;\r
8784   char fileTitle[MSG_SIZ];\r
8785 \r
8786   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8787   f = OpenFileDialog(hwndMain, "a", defName,\r
8788                      appData.oldSaveStyle ? "gam" : "pgn",\r
8789                      GAME_FILT, \r
8790                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8791   if (f != NULL) {\r
8792     SaveGame(f, 0, "");\r
8793     fclose(f);\r
8794   }\r
8795 }\r
8796 \r
8797 \r
8798 void\r
8799 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8800 {\r
8801   if (delayedTimerEvent != 0) {\r
8802     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8803       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8804     }\r
8805     KillTimer(hwndMain, delayedTimerEvent);\r
8806     delayedTimerEvent = 0;\r
8807     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8808     delayedTimerCallback();\r
8809   }\r
8810   delayedTimerCallback = cb;\r
8811   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8812                                 (UINT) millisec, NULL);\r
8813 }\r
8814 \r
8815 DelayedEventCallback\r
8816 GetDelayedEvent()\r
8817 {\r
8818   if (delayedTimerEvent) {\r
8819     return delayedTimerCallback;\r
8820   } else {\r
8821     return NULL;\r
8822   }\r
8823 }\r
8824 \r
8825 void\r
8826 CancelDelayedEvent()\r
8827 {\r
8828   if (delayedTimerEvent) {\r
8829     KillTimer(hwndMain, delayedTimerEvent);\r
8830     delayedTimerEvent = 0;\r
8831   }\r
8832 }\r
8833 \r
8834 DWORD GetWin32Priority(int nice)\r
8835 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8836 /*\r
8837 REALTIME_PRIORITY_CLASS     0x00000100\r
8838 HIGH_PRIORITY_CLASS         0x00000080\r
8839 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8840 NORMAL_PRIORITY_CLASS       0x00000020\r
8841 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8842 IDLE_PRIORITY_CLASS         0x00000040\r
8843 */\r
8844         if (nice < -15) return 0x00000080;\r
8845         if (nice < 0)   return 0x00008000;\r
8846         if (nice == 0)  return 0x00000020;\r
8847         if (nice < 15)  return 0x00004000;\r
8848         return 0x00000040;\r
8849 }\r
8850 \r
8851 /* Start a child process running the given program.\r
8852    The process's standard output can be read from "from", and its\r
8853    standard input can be written to "to".\r
8854    Exit with fatal error if anything goes wrong.\r
8855    Returns an opaque pointer that can be used to destroy the process\r
8856    later.\r
8857 */\r
8858 int\r
8859 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8860 {\r
8861 #define BUFSIZE 4096\r
8862 \r
8863   HANDLE hChildStdinRd, hChildStdinWr,\r
8864     hChildStdoutRd, hChildStdoutWr;\r
8865   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8866   SECURITY_ATTRIBUTES saAttr;\r
8867   BOOL fSuccess;\r
8868   PROCESS_INFORMATION piProcInfo;\r
8869   STARTUPINFO siStartInfo;\r
8870   ChildProc *cp;\r
8871   char buf[MSG_SIZ];\r
8872   DWORD err;\r
8873 \r
8874   if (appData.debugMode) {\r
8875     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8876   }\r
8877 \r
8878   *pr = NoProc;\r
8879 \r
8880   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8881   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8882   saAttr.bInheritHandle = TRUE;\r
8883   saAttr.lpSecurityDescriptor = NULL;\r
8884 \r
8885   /*\r
8886    * The steps for redirecting child's STDOUT:\r
8887    *     1. Create anonymous pipe to be STDOUT for child.\r
8888    *     2. Create a noninheritable duplicate of read handle,\r
8889    *         and close the inheritable read handle.\r
8890    */\r
8891 \r
8892   /* Create a pipe for the child's STDOUT. */\r
8893   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8894     return GetLastError();\r
8895   }\r
8896 \r
8897   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8898   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8899                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8900                              FALSE,     /* not inherited */\r
8901                              DUPLICATE_SAME_ACCESS);\r
8902   if (! fSuccess) {\r
8903     return GetLastError();\r
8904   }\r
8905   CloseHandle(hChildStdoutRd);\r
8906 \r
8907   /*\r
8908    * The steps for redirecting child's STDIN:\r
8909    *     1. Create anonymous pipe to be STDIN for child.\r
8910    *     2. Create a noninheritable duplicate of write handle,\r
8911    *         and close the inheritable write handle.\r
8912    */\r
8913 \r
8914   /* Create a pipe for the child's STDIN. */\r
8915   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8916     return GetLastError();\r
8917   }\r
8918 \r
8919   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8920   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8921                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8922                              FALSE,     /* not inherited */\r
8923                              DUPLICATE_SAME_ACCESS);\r
8924   if (! fSuccess) {\r
8925     return GetLastError();\r
8926   }\r
8927   CloseHandle(hChildStdinWr);\r
8928 \r
8929   /* Arrange to (1) look in dir for the child .exe file, and\r
8930    * (2) have dir be the child's working directory.  Interpret\r
8931    * dir relative to the directory WinBoard loaded from. */\r
8932   GetCurrentDirectory(MSG_SIZ, buf);\r
8933   SetCurrentDirectory(installDir);\r
8934   SetCurrentDirectory(dir);\r
8935 \r
8936   /* Now create the child process. */\r
8937 \r
8938   siStartInfo.cb = sizeof(STARTUPINFO);\r
8939   siStartInfo.lpReserved = NULL;\r
8940   siStartInfo.lpDesktop = NULL;\r
8941   siStartInfo.lpTitle = NULL;\r
8942   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8943   siStartInfo.cbReserved2 = 0;\r
8944   siStartInfo.lpReserved2 = NULL;\r
8945   siStartInfo.hStdInput = hChildStdinRd;\r
8946   siStartInfo.hStdOutput = hChildStdoutWr;\r
8947   siStartInfo.hStdError = hChildStdoutWr;\r
8948 \r
8949   fSuccess = CreateProcess(NULL,\r
8950                            cmdLine,        /* command line */\r
8951                            NULL,           /* process security attributes */\r
8952                            NULL,           /* primary thread security attrs */\r
8953                            TRUE,           /* handles are inherited */\r
8954                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8955                            NULL,           /* use parent's environment */\r
8956                            NULL,\r
8957                            &siStartInfo, /* STARTUPINFO pointer */\r
8958                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8959 \r
8960   err = GetLastError();\r
8961   SetCurrentDirectory(buf); /* return to prev directory */\r
8962   if (! fSuccess) {\r
8963     return err;\r
8964   }\r
8965 \r
8966   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8967     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8968     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8969   }\r
8970 \r
8971   /* Close the handles we don't need in the parent */\r
8972   CloseHandle(piProcInfo.hThread);\r
8973   CloseHandle(hChildStdinRd);\r
8974   CloseHandle(hChildStdoutWr);\r
8975 \r
8976   /* Prepare return value */\r
8977   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8978   cp->kind = CPReal;\r
8979   cp->hProcess = piProcInfo.hProcess;\r
8980   cp->pid = piProcInfo.dwProcessId;\r
8981   cp->hFrom = hChildStdoutRdDup;\r
8982   cp->hTo = hChildStdinWrDup;\r
8983 \r
8984   *pr = (void *) cp;\r
8985 \r
8986   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8987      2000 where engines sometimes don't see the initial command(s)\r
8988      from WinBoard and hang.  I don't understand how that can happen,\r
8989      but the Sleep is harmless, so I've put it in.  Others have also\r
8990      reported what may be the same problem, so hopefully this will fix\r
8991      it for them too.  */\r
8992   Sleep(500);\r
8993 \r
8994   return NO_ERROR;\r
8995 }\r
8996 \r
8997 \r
8998 void\r
8999 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9000 {\r
9001   ChildProc *cp; int result;\r
9002 \r
9003   cp = (ChildProc *) pr;\r
9004   if (cp == NULL) return;\r
9005 \r
9006   switch (cp->kind) {\r
9007   case CPReal:\r
9008     /* TerminateProcess is considered harmful, so... */\r
9009     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9010     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9011     /* The following doesn't work because the chess program\r
9012        doesn't "have the same console" as WinBoard.  Maybe\r
9013        we could arrange for this even though neither WinBoard\r
9014        nor the chess program uses a console for stdio? */\r
9015     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9016 \r
9017     /* [AS] Special termination modes for misbehaving programs... */\r
9018     if( signal == 9 ) { \r
9019         result = TerminateProcess( cp->hProcess, 0 );\r
9020 \r
9021         if ( appData.debugMode) {\r
9022             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9023         }\r
9024     }\r
9025     else if( signal == 10 ) {\r
9026         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9027 \r
9028         if( dw != WAIT_OBJECT_0 ) {\r
9029             result = TerminateProcess( cp->hProcess, 0 );\r
9030 \r
9031             if ( appData.debugMode) {\r
9032                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9033             }\r
9034 \r
9035         }\r
9036     }\r
9037 \r
9038     CloseHandle(cp->hProcess);\r
9039     break;\r
9040 \r
9041   case CPComm:\r
9042     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9043     break;\r
9044 \r
9045   case CPSock:\r
9046     closesocket(cp->sock);\r
9047     WSACleanup();\r
9048     break;\r
9049 \r
9050   case CPRcmd:\r
9051     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9052     closesocket(cp->sock);\r
9053     closesocket(cp->sock2);\r
9054     WSACleanup();\r
9055     break;\r
9056   }\r
9057   free(cp);\r
9058 }\r
9059 \r
9060 void\r
9061 InterruptChildProcess(ProcRef pr)\r
9062 {\r
9063   ChildProc *cp;\r
9064 \r
9065   cp = (ChildProc *) pr;\r
9066   if (cp == NULL) return;\r
9067   switch (cp->kind) {\r
9068   case CPReal:\r
9069     /* The following doesn't work because the chess program\r
9070        doesn't "have the same console" as WinBoard.  Maybe\r
9071        we could arrange for this even though neither WinBoard\r
9072        nor the chess program uses a console for stdio */\r
9073     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9074     break;\r
9075 \r
9076   case CPComm:\r
9077   case CPSock:\r
9078     /* Can't interrupt */\r
9079     break;\r
9080 \r
9081   case CPRcmd:\r
9082     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9083     break;\r
9084   }\r
9085 }\r
9086 \r
9087 \r
9088 int\r
9089 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9090 {\r
9091   char cmdLine[MSG_SIZ];\r
9092 \r
9093   if (port[0] == NULLCHAR) {\r
9094     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9095   } else {\r
9096     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9097   }\r
9098   return StartChildProcess(cmdLine, "", pr);\r
9099 }\r
9100 \r
9101 \r
9102 /* Code to open TCP sockets */\r
9103 \r
9104 int\r
9105 OpenTCP(char *host, char *port, ProcRef *pr)\r
9106 {\r
9107   ChildProc *cp;\r
9108   int err;\r
9109   SOCKET s;\r
9110   struct sockaddr_in sa, mysa;\r
9111   struct hostent FAR *hp;\r
9112   unsigned short uport;\r
9113   WORD wVersionRequested;\r
9114   WSADATA wsaData;\r
9115 \r
9116   /* Initialize socket DLL */\r
9117   wVersionRequested = MAKEWORD(1, 1);\r
9118   err = WSAStartup(wVersionRequested, &wsaData);\r
9119   if (err != 0) return err;\r
9120 \r
9121   /* Make socket */\r
9122   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9123     err = WSAGetLastError();\r
9124     WSACleanup();\r
9125     return err;\r
9126   }\r
9127 \r
9128   /* Bind local address using (mostly) don't-care values.\r
9129    */\r
9130   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9131   mysa.sin_family = AF_INET;\r
9132   mysa.sin_addr.s_addr = INADDR_ANY;\r
9133   uport = (unsigned short) 0;\r
9134   mysa.sin_port = htons(uport);\r
9135   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9136       == SOCKET_ERROR) {\r
9137     err = WSAGetLastError();\r
9138     WSACleanup();\r
9139     return err;\r
9140   }\r
9141 \r
9142   /* Resolve remote host name */\r
9143   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9144   if (!(hp = gethostbyname(host))) {\r
9145     unsigned int b0, b1, b2, b3;\r
9146 \r
9147     err = WSAGetLastError();\r
9148 \r
9149     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9150       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9151       hp->h_addrtype = AF_INET;\r
9152       hp->h_length = 4;\r
9153       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9154       hp->h_addr_list[0] = (char *) malloc(4);\r
9155       hp->h_addr_list[0][0] = (char) b0;\r
9156       hp->h_addr_list[0][1] = (char) b1;\r
9157       hp->h_addr_list[0][2] = (char) b2;\r
9158       hp->h_addr_list[0][3] = (char) b3;\r
9159     } else {\r
9160       WSACleanup();\r
9161       return err;\r
9162     }\r
9163   }\r
9164   sa.sin_family = hp->h_addrtype;\r
9165   uport = (unsigned short) atoi(port);\r
9166   sa.sin_port = htons(uport);\r
9167   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9168 \r
9169   /* Make connection */\r
9170   if (connect(s, (struct sockaddr *) &sa,\r
9171               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9172     err = WSAGetLastError();\r
9173     WSACleanup();\r
9174     return err;\r
9175   }\r
9176 \r
9177   /* Prepare return value */\r
9178   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9179   cp->kind = CPSock;\r
9180   cp->sock = s;\r
9181   *pr = (ProcRef *) cp;\r
9182 \r
9183   return NO_ERROR;\r
9184 }\r
9185 \r
9186 int\r
9187 OpenCommPort(char *name, ProcRef *pr)\r
9188 {\r
9189   HANDLE h;\r
9190   COMMTIMEOUTS ct;\r
9191   ChildProc *cp;\r
9192   char fullname[MSG_SIZ];\r
9193 \r
9194   if (*name != '\\')\r
9195     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9196   else\r
9197     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9198 \r
9199   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9200                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9201   if (h == (HANDLE) -1) {\r
9202     return GetLastError();\r
9203   }\r
9204   hCommPort = h;\r
9205 \r
9206   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9207 \r
9208   /* Accumulate characters until a 100ms pause, then parse */\r
9209   ct.ReadIntervalTimeout = 100;\r
9210   ct.ReadTotalTimeoutMultiplier = 0;\r
9211   ct.ReadTotalTimeoutConstant = 0;\r
9212   ct.WriteTotalTimeoutMultiplier = 0;\r
9213   ct.WriteTotalTimeoutConstant = 0;\r
9214   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9215 \r
9216   /* Prepare return value */\r
9217   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9218   cp->kind = CPComm;\r
9219   cp->hFrom = h;\r
9220   cp->hTo = h;\r
9221   *pr = (ProcRef *) cp;\r
9222 \r
9223   return NO_ERROR;\r
9224 }\r
9225 \r
9226 int\r
9227 OpenLoopback(ProcRef *pr)\r
9228 {\r
9229   DisplayFatalError(_("Not implemented"), 0, 1);\r
9230   return NO_ERROR;\r
9231 }\r
9232 \r
9233 \r
9234 int\r
9235 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9236 {\r
9237   ChildProc *cp;\r
9238   int err;\r
9239   SOCKET s, s2, s3;\r
9240   struct sockaddr_in sa, mysa;\r
9241   struct hostent FAR *hp;\r
9242   unsigned short uport;\r
9243   WORD wVersionRequested;\r
9244   WSADATA wsaData;\r
9245   int fromPort;\r
9246   char stderrPortStr[MSG_SIZ];\r
9247 \r
9248   /* Initialize socket DLL */\r
9249   wVersionRequested = MAKEWORD(1, 1);\r
9250   err = WSAStartup(wVersionRequested, &wsaData);\r
9251   if (err != 0) return err;\r
9252 \r
9253   /* Resolve remote host name */\r
9254   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9255   if (!(hp = gethostbyname(host))) {\r
9256     unsigned int b0, b1, b2, b3;\r
9257 \r
9258     err = WSAGetLastError();\r
9259 \r
9260     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9261       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9262       hp->h_addrtype = AF_INET;\r
9263       hp->h_length = 4;\r
9264       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9265       hp->h_addr_list[0] = (char *) malloc(4);\r
9266       hp->h_addr_list[0][0] = (char) b0;\r
9267       hp->h_addr_list[0][1] = (char) b1;\r
9268       hp->h_addr_list[0][2] = (char) b2;\r
9269       hp->h_addr_list[0][3] = (char) b3;\r
9270     } else {\r
9271       WSACleanup();\r
9272       return err;\r
9273     }\r
9274   }\r
9275   sa.sin_family = hp->h_addrtype;\r
9276   uport = (unsigned short) 514;\r
9277   sa.sin_port = htons(uport);\r
9278   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9279 \r
9280   /* Bind local socket to unused "privileged" port address\r
9281    */\r
9282   s = INVALID_SOCKET;\r
9283   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9284   mysa.sin_family = AF_INET;\r
9285   mysa.sin_addr.s_addr = INADDR_ANY;\r
9286   for (fromPort = 1023;; fromPort--) {\r
9287     if (fromPort < 0) {\r
9288       WSACleanup();\r
9289       return WSAEADDRINUSE;\r
9290     }\r
9291     if (s == INVALID_SOCKET) {\r
9292       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9293         err = WSAGetLastError();\r
9294         WSACleanup();\r
9295         return err;\r
9296       }\r
9297     }\r
9298     uport = (unsigned short) fromPort;\r
9299     mysa.sin_port = htons(uport);\r
9300     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9301         == SOCKET_ERROR) {\r
9302       err = WSAGetLastError();\r
9303       if (err == WSAEADDRINUSE) continue;\r
9304       WSACleanup();\r
9305       return err;\r
9306     }\r
9307     if (connect(s, (struct sockaddr *) &sa,\r
9308       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9309       err = WSAGetLastError();\r
9310       if (err == WSAEADDRINUSE) {\r
9311         closesocket(s);\r
9312         s = -1;\r
9313         continue;\r
9314       }\r
9315       WSACleanup();\r
9316       return err;\r
9317     }\r
9318     break;\r
9319   }\r
9320 \r
9321   /* Bind stderr local socket to unused "privileged" port address\r
9322    */\r
9323   s2 = INVALID_SOCKET;\r
9324   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9325   mysa.sin_family = AF_INET;\r
9326   mysa.sin_addr.s_addr = INADDR_ANY;\r
9327   for (fromPort = 1023;; fromPort--) {\r
9328     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9329     if (fromPort < 0) {\r
9330       (void) closesocket(s);\r
9331       WSACleanup();\r
9332       return WSAEADDRINUSE;\r
9333     }\r
9334     if (s2 == INVALID_SOCKET) {\r
9335       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9336         err = WSAGetLastError();\r
9337         closesocket(s);\r
9338         WSACleanup();\r
9339         return err;\r
9340       }\r
9341     }\r
9342     uport = (unsigned short) fromPort;\r
9343     mysa.sin_port = htons(uport);\r
9344     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9345         == SOCKET_ERROR) {\r
9346       err = WSAGetLastError();\r
9347       if (err == WSAEADDRINUSE) continue;\r
9348       (void) closesocket(s);\r
9349       WSACleanup();\r
9350       return err;\r
9351     }\r
9352     if (listen(s2, 1) == SOCKET_ERROR) {\r
9353       err = WSAGetLastError();\r
9354       if (err == WSAEADDRINUSE) {\r
9355         closesocket(s2);\r
9356         s2 = INVALID_SOCKET;\r
9357         continue;\r
9358       }\r
9359       (void) closesocket(s);\r
9360       (void) closesocket(s2);\r
9361       WSACleanup();\r
9362       return err;\r
9363     }\r
9364     break;\r
9365   }\r
9366   prevStderrPort = fromPort; // remember port used\r
9367   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9368 \r
9369   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9370     err = WSAGetLastError();\r
9371     (void) closesocket(s);\r
9372     (void) closesocket(s2);\r
9373     WSACleanup();\r
9374     return err;\r
9375   }\r
9376 \r
9377   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9378     err = WSAGetLastError();\r
9379     (void) closesocket(s);\r
9380     (void) closesocket(s2);\r
9381     WSACleanup();\r
9382     return err;\r
9383   }\r
9384   if (*user == NULLCHAR) user = UserName();\r
9385   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9386     err = WSAGetLastError();\r
9387     (void) closesocket(s);\r
9388     (void) closesocket(s2);\r
9389     WSACleanup();\r
9390     return err;\r
9391   }\r
9392   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9393     err = WSAGetLastError();\r
9394     (void) closesocket(s);\r
9395     (void) closesocket(s2);\r
9396     WSACleanup();\r
9397     return err;\r
9398   }\r
9399 \r
9400   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9401     err = WSAGetLastError();\r
9402     (void) closesocket(s);\r
9403     (void) closesocket(s2);\r
9404     WSACleanup();\r
9405     return err;\r
9406   }\r
9407   (void) closesocket(s2);  /* Stop listening */\r
9408 \r
9409   /* Prepare return value */\r
9410   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9411   cp->kind = CPRcmd;\r
9412   cp->sock = s;\r
9413   cp->sock2 = s3;\r
9414   *pr = (ProcRef *) cp;\r
9415 \r
9416   return NO_ERROR;\r
9417 }\r
9418 \r
9419 \r
9420 InputSourceRef\r
9421 AddInputSource(ProcRef pr, int lineByLine,\r
9422                InputCallback func, VOIDSTAR closure)\r
9423 {\r
9424   InputSource *is, *is2 = NULL;\r
9425   ChildProc *cp = (ChildProc *) pr;\r
9426 \r
9427   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9428   is->lineByLine = lineByLine;\r
9429   is->func = func;\r
9430   is->closure = closure;\r
9431   is->second = NULL;\r
9432   is->next = is->buf;\r
9433   if (pr == NoProc) {\r
9434     is->kind = CPReal;\r
9435     consoleInputSource = is;\r
9436   } else {\r
9437     is->kind = cp->kind;\r
9438     /* \r
9439         [AS] Try to avoid a race condition if the thread is given control too early:\r
9440         we create all threads suspended so that the is->hThread variable can be\r
9441         safely assigned, then let the threads start with ResumeThread.\r
9442     */\r
9443     switch (cp->kind) {\r
9444     case CPReal:\r
9445       is->hFile = cp->hFrom;\r
9446       cp->hFrom = NULL; /* now owned by InputThread */\r
9447       is->hThread =\r
9448         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9449                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9450       break;\r
9451 \r
9452     case CPComm:\r
9453       is->hFile = cp->hFrom;\r
9454       cp->hFrom = NULL; /* now owned by InputThread */\r
9455       is->hThread =\r
9456         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9457                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9458       break;\r
9459 \r
9460     case CPSock:\r
9461       is->sock = cp->sock;\r
9462       is->hThread =\r
9463         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9464                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9465       break;\r
9466 \r
9467     case CPRcmd:\r
9468       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9469       *is2 = *is;\r
9470       is->sock = cp->sock;\r
9471       is->second = is2;\r
9472       is2->sock = cp->sock2;\r
9473       is2->second = is2;\r
9474       is->hThread =\r
9475         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9476                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9477       is2->hThread =\r
9478         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9479                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9480       break;\r
9481     }\r
9482 \r
9483     if( is->hThread != NULL ) {\r
9484         ResumeThread( is->hThread );\r
9485     }\r
9486 \r
9487     if( is2 != NULL && is2->hThread != NULL ) {\r
9488         ResumeThread( is2->hThread );\r
9489     }\r
9490   }\r
9491 \r
9492   return (InputSourceRef) is;\r
9493 }\r
9494 \r
9495 void\r
9496 RemoveInputSource(InputSourceRef isr)\r
9497 {\r
9498   InputSource *is;\r
9499 \r
9500   is = (InputSource *) isr;\r
9501   is->hThread = NULL;  /* tell thread to stop */\r
9502   CloseHandle(is->hThread);\r
9503   if (is->second != NULL) {\r
9504     is->second->hThread = NULL;\r
9505     CloseHandle(is->second->hThread);\r
9506   }\r
9507 }\r
9508 \r
9509 int no_wrap(char *message, int count)\r
9510 {\r
9511     ConsoleOutput(message, count, FALSE);\r
9512     return count;\r
9513 }\r
9514 \r
9515 int\r
9516 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9517 {\r
9518   DWORD dOutCount;\r
9519   int outCount = SOCKET_ERROR;\r
9520   ChildProc *cp = (ChildProc *) pr;\r
9521   static OVERLAPPED ovl;\r
9522   static int line = 0;\r
9523 \r
9524   if (pr == NoProc)\r
9525   {\r
9526     if (appData.noJoin || !appData.useInternalWrap)\r
9527       return no_wrap(message, count);\r
9528     else\r
9529     {\r
9530       int width = get_term_width();\r
9531       int len = wrap(NULL, message, count, width, &line);\r
9532       char *msg = malloc(len);\r
9533       int dbgchk;\r
9534 \r
9535       if (!msg)\r
9536         return no_wrap(message, count);\r
9537       else\r
9538       {\r
9539         dbgchk = wrap(msg, message, count, width, &line);\r
9540         if (dbgchk != len && appData.debugMode)\r
9541             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9542         ConsoleOutput(msg, len, FALSE);\r
9543         free(msg);\r
9544         return len;\r
9545       }\r
9546     }\r
9547   }\r
9548 \r
9549   if (ovl.hEvent == NULL) {\r
9550     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9551   }\r
9552   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9553 \r
9554   switch (cp->kind) {\r
9555   case CPSock:\r
9556   case CPRcmd:\r
9557     outCount = send(cp->sock, message, count, 0);\r
9558     if (outCount == SOCKET_ERROR) {\r
9559       *outError = WSAGetLastError();\r
9560     } else {\r
9561       *outError = NO_ERROR;\r
9562     }\r
9563     break;\r
9564 \r
9565   case CPReal:\r
9566     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9567                   &dOutCount, NULL)) {\r
9568       *outError = NO_ERROR;\r
9569       outCount = (int) dOutCount;\r
9570     } else {\r
9571       *outError = GetLastError();\r
9572     }\r
9573     break;\r
9574 \r
9575   case CPComm:\r
9576     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9577                             &dOutCount, &ovl);\r
9578     if (*outError == NO_ERROR) {\r
9579       outCount = (int) dOutCount;\r
9580     }\r
9581     break;\r
9582   }\r
9583   return outCount;\r
9584 }\r
9585 \r
9586 int\r
9587 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9588                        long msdelay)\r
9589 {\r
9590   /* Ignore delay, not implemented for WinBoard */\r
9591   return OutputToProcess(pr, message, count, outError);\r
9592 }\r
9593 \r
9594 \r
9595 void\r
9596 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9597                         char *buf, int count, int error)\r
9598 {\r
9599   DisplayFatalError(_("Not implemented"), 0, 1);\r
9600 }\r
9601 \r
9602 /* see wgamelist.c for Game List functions */\r
9603 /* see wedittags.c for Edit Tags functions */\r
9604 \r
9605 \r
9606 VOID\r
9607 ICSInitScript()\r
9608 {\r
9609   FILE *f;\r
9610   char buf[MSG_SIZ];\r
9611   char *dummy;\r
9612 \r
9613   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9614     f = fopen(buf, "r");\r
9615     if (f != NULL) {\r
9616       ProcessICSInitScript(f);\r
9617       fclose(f);\r
9618     }\r
9619   }\r
9620 }\r
9621 \r
9622 \r
9623 VOID\r
9624 StartAnalysisClock()\r
9625 {\r
9626   if (analysisTimerEvent) return;\r
9627   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9628                                         (UINT) 2000, NULL);\r
9629 }\r
9630 \r
9631 VOID\r
9632 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9633 {\r
9634   highlightInfo.sq[0].x = fromX;\r
9635   highlightInfo.sq[0].y = fromY;\r
9636   highlightInfo.sq[1].x = toX;\r
9637   highlightInfo.sq[1].y = toY;\r
9638 }\r
9639 \r
9640 VOID\r
9641 ClearHighlights()\r
9642 {\r
9643   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9644     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9645 }\r
9646 \r
9647 VOID\r
9648 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9649 {\r
9650   premoveHighlightInfo.sq[0].x = fromX;\r
9651   premoveHighlightInfo.sq[0].y = fromY;\r
9652   premoveHighlightInfo.sq[1].x = toX;\r
9653   premoveHighlightInfo.sq[1].y = toY;\r
9654 }\r
9655 \r
9656 VOID\r
9657 ClearPremoveHighlights()\r
9658 {\r
9659   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9660     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9661 }\r
9662 \r
9663 VOID\r
9664 ShutDownFrontEnd()\r
9665 {\r
9666   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9667   DeleteClipboardTempFiles();\r
9668 }\r
9669 \r
9670 void\r
9671 BoardToTop()\r
9672 {\r
9673     if (IsIconic(hwndMain))\r
9674       ShowWindow(hwndMain, SW_RESTORE);\r
9675 \r
9676     SetActiveWindow(hwndMain);\r
9677 }\r
9678 \r
9679 /*\r
9680  * Prototypes for animation support routines\r
9681  */\r
9682 static void ScreenSquare(int column, int row, POINT * pt);\r
9683 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9684      POINT frames[], int * nFrames);\r
9685 \r
9686 \r
9687 #define kFactor 4\r
9688 \r
9689 void\r
9690 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9691 {       // [HGM] atomic: animate blast wave\r
9692         int i;\r
9693 \r
9694         explodeInfo.fromX = fromX;\r
9695         explodeInfo.fromY = fromY;\r
9696         explodeInfo.toX = toX;\r
9697         explodeInfo.toY = toY;\r
9698         for(i=1; i<4*kFactor; i++) {\r
9699             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9700             DrawPosition(FALSE, board);\r
9701             Sleep(appData.animSpeed);\r
9702         }\r
9703         explodeInfo.radius = 0;\r
9704         DrawPosition(TRUE, board);\r
9705 }\r
9706 \r
9707 void\r
9708 AnimateMove(board, fromX, fromY, toX, toY)\r
9709      Board board;\r
9710      int fromX;\r
9711      int fromY;\r
9712      int toX;\r
9713      int toY;\r
9714 {\r
9715   ChessSquare piece;\r
9716   POINT start, finish, mid;\r
9717   POINT frames[kFactor * 2 + 1];\r
9718   int nFrames, n;\r
9719 \r
9720   if (!appData.animate) return;\r
9721   if (doingSizing) return;\r
9722   if (fromY < 0 || fromX < 0) return;\r
9723   piece = board[fromY][fromX];\r
9724   if (piece >= EmptySquare) return;\r
9725 \r
9726   ScreenSquare(fromX, fromY, &start);\r
9727   ScreenSquare(toX, toY, &finish);\r
9728 \r
9729   /* All moves except knight jumps move in straight line */\r
9730   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9731     mid.x = start.x + (finish.x - start.x) / 2;\r
9732     mid.y = start.y + (finish.y - start.y) / 2;\r
9733   } else {\r
9734     /* Knight: make straight movement then diagonal */\r
9735     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9736        mid.x = start.x + (finish.x - start.x) / 2;\r
9737        mid.y = start.y;\r
9738      } else {\r
9739        mid.x = start.x;\r
9740        mid.y = start.y + (finish.y - start.y) / 2;\r
9741      }\r
9742   }\r
9743   \r
9744   /* Don't use as many frames for very short moves */\r
9745   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9746     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9747   else\r
9748     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9749 \r
9750   animInfo.from.x = fromX;\r
9751   animInfo.from.y = fromY;\r
9752   animInfo.to.x = toX;\r
9753   animInfo.to.y = toY;\r
9754   animInfo.lastpos = start;\r
9755   animInfo.piece = piece;\r
9756   for (n = 0; n < nFrames; n++) {\r
9757     animInfo.pos = frames[n];\r
9758     DrawPosition(FALSE, NULL);\r
9759     animInfo.lastpos = animInfo.pos;\r
9760     Sleep(appData.animSpeed);\r
9761   }\r
9762   animInfo.pos = finish;\r
9763   DrawPosition(FALSE, NULL);\r
9764   animInfo.piece = EmptySquare;\r
9765   Explode(board, fromX, fromY, toX, toY);\r
9766 }\r
9767 \r
9768 /*      Convert board position to corner of screen rect and color       */\r
9769 \r
9770 static void\r
9771 ScreenSquare(column, row, pt)\r
9772      int column; int row; POINT * pt;\r
9773 {\r
9774   if (flipView) {\r
9775     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9776     pt->y = lineGap + row * (squareSize + lineGap);\r
9777   } else {\r
9778     pt->x = lineGap + column * (squareSize + lineGap);\r
9779     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9780   }\r
9781 }\r
9782 \r
9783 /*      Generate a series of frame coords from start->mid->finish.\r
9784         The movement rate doubles until the half way point is\r
9785         reached, then halves back down to the final destination,\r
9786         which gives a nice slow in/out effect. The algorithmn\r
9787         may seem to generate too many intermediates for short\r
9788         moves, but remember that the purpose is to attract the\r
9789         viewers attention to the piece about to be moved and\r
9790         then to where it ends up. Too few frames would be less\r
9791         noticeable.                                             */\r
9792 \r
9793 static void\r
9794 Tween(start, mid, finish, factor, frames, nFrames)\r
9795      POINT * start; POINT * mid;\r
9796      POINT * finish; int factor;\r
9797      POINT frames[]; int * nFrames;\r
9798 {\r
9799   int n, fraction = 1, count = 0;\r
9800 \r
9801   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9802   for (n = 0; n < factor; n++)\r
9803     fraction *= 2;\r
9804   for (n = 0; n < factor; n++) {\r
9805     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9806     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9807     count ++;\r
9808     fraction = fraction / 2;\r
9809   }\r
9810   \r
9811   /* Midpoint */\r
9812   frames[count] = *mid;\r
9813   count ++;\r
9814   \r
9815   /* Slow out, stepping 1/2, then 1/4, ... */\r
9816   fraction = 2;\r
9817   for (n = 0; n < factor; n++) {\r
9818     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9819     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9820     count ++;\r
9821     fraction = fraction * 2;\r
9822   }\r
9823   *nFrames = count;\r
9824 }\r
9825 \r
9826 void\r
9827 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9828 {\r
9829     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9830 \r
9831     EvalGraphSet( first, last, current, pvInfoList );\r
9832 }\r
9833 \r
9834 void\r
9835 SettingsPopUp(ChessProgramState *cps)\r
9836 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9837       EngineOptionsPopup(savedHwnd, cps);\r
9838 }\r