Let Engine #2 Settings start second engine
[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, 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 }, \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,\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[][8];\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(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);\r
402     if(!barbaric) return;\r
403     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
404     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
405     GetWindowText( hDlg, buf, MSG_SIZ );\r
406     s = T_(buf);\r
407 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);\r
408     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
409     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
410         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
411         if(strlen(buf) == 0) continue;\r
412         s = T_(buf);\r
413         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
414     }\r
415 }\r
416 \r
417 void\r
418 TranslateMenus(int addLanguage)\r
419 {\r
420     int i, j;\r
421     WIN32_FIND_DATA fileData;\r
422     HANDLE hFind;\r
423 #define IDM_English 1895\r
424     if(1) {\r
425         HMENU mainMenu = GetMenu(hwndMain);\r
426         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
427           HMENU subMenu = GetSubMenu(mainMenu, i);\r
428           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
429                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
430           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
431             char buf[MSG_SIZ];\r
432             UINT k = GetMenuItemID(subMenu, j);\r
433               if(menuText[i][j])
434                 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {\r
435                 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
436                 menuText[i][j] = strdup(buf); // remember original on first change\r
437             }\r
438             if(buf[0] == NULLCHAR) continue;\r
439 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);\r
440             ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION\r
441                                    |CheckMenuItem(subMenu, j, MF_BYPOSITION)\r
442                                    |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));\r
443           }\r
444         }\r
445         DrawMenuBar(hwndMain);\r
446     }\r
447 \r
448     if(!addLanguage) return;\r
449     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
450         HMENU mainMenu = GetMenu(hwndMain);\r
451         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
452         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
453         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
454         i = 0; lastChecked = IDM_English;\r
455         do {\r
456             char *p, *q = fileData.cFileName;\r
457             int checkFlag = MF_UNCHECKED;\r
458             languageFile[i] = strdup(q);\r
459             if(barbaric && !strcmp(oldLanguage, q)) {\r
460                 checkFlag = MF_CHECKED;\r
461                 lastChecked = IDM_English + i + 1;\r
462                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
463             }\r
464             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
465             p = strstr(fileData.cFileName, ".lng");\r
466             if(p) *p = 0;\r
467             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
468         } while(FindNextFile(hFind, &fileData));\r
469         FindClose(hFind);\r
470     }\r
471 }\r
472 \r
473 #endif\r
474 \r
475 typedef struct {\r
476   char *name;\r
477   int squareSize;\r
478   int lineGap;\r
479   int smallLayout;\r
480   int tinyLayout;\r
481   int cliWidth, cliHeight;\r
482 } SizeInfo;\r
483 \r
484 SizeInfo sizeInfo[] = \r
485 {\r
486   { "tiny",     21, 0, 1, 1, 0, 0 },\r
487   { "teeny",    25, 1, 1, 1, 0, 0 },\r
488   { "dinky",    29, 1, 1, 1, 0, 0 },\r
489   { "petite",   33, 1, 1, 1, 0, 0 },\r
490   { "slim",     37, 2, 1, 0, 0, 0 },\r
491   { "small",    40, 2, 1, 0, 0, 0 },\r
492   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
493   { "middling", 49, 2, 0, 0, 0, 0 },\r
494   { "average",  54, 2, 0, 0, 0, 0 },\r
495   { "moderate", 58, 3, 0, 0, 0, 0 },\r
496   { "medium",   64, 3, 0, 0, 0, 0 },\r
497   { "bulky",    72, 3, 0, 0, 0, 0 },\r
498   { "large",    80, 3, 0, 0, 0, 0 },\r
499   { "big",      87, 3, 0, 0, 0, 0 },\r
500   { "huge",     95, 3, 0, 0, 0, 0 },\r
501   { "giant",    108, 3, 0, 0, 0, 0 },\r
502   { "colossal", 116, 4, 0, 0, 0, 0 },\r
503   { "titanic",  129, 4, 0, 0, 0, 0 },\r
504   { NULL, 0, 0, 0, 0, 0, 0 }\r
505 };\r
506 \r
507 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
508 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
509 {\r
510   { 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
511   { 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
512   { 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
513   { 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
514   { 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
515   { 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
516   { 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
517   { 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
518   { 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
519   { 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
520   { 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
521   { 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
522   { 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
523   { 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
524   { 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
525   { 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
526   { 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
527   { 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
528 };\r
529 \r
530 MyFont *font[NUM_SIZES][NUM_FONTS];\r
531 \r
532 typedef struct {\r
533   char *label;\r
534   int id;\r
535   HWND hwnd;\r
536   WNDPROC wndproc;\r
537 } MyButtonDesc;\r
538 \r
539 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
540 #define N_BUTTONS 5\r
541 \r
542 MyButtonDesc buttonDesc[N_BUTTONS] =\r
543 {\r
544   {"<<", IDM_ToStart, NULL, NULL},\r
545   {"<", IDM_Backward, NULL, NULL},\r
546   {"P", IDM_Pause, NULL, NULL},\r
547   {">", IDM_Forward, NULL, NULL},\r
548   {">>", IDM_ToEnd, NULL, NULL},\r
549 };\r
550 \r
551 int tinyLayout = 0, smallLayout = 0;\r
552 #define MENU_BAR_ITEMS 7\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
554   { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },\r
555   { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },\r
556 };\r
557 \r
558 \r
559 MySound sounds[(int)NSoundClasses];\r
560 MyTextAttribs textAttribs[(int)NColorClasses];\r
561 \r
562 MyColorizeAttribs colorizeAttribs[] = {\r
563   { (COLORREF)0, 0, N_("Shout Text") },\r
564   { (COLORREF)0, 0, N_("SShout/CShout") },\r
565   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
566   { (COLORREF)0, 0, N_("Channel Text") },\r
567   { (COLORREF)0, 0, N_("Kibitz Text") },\r
568   { (COLORREF)0, 0, N_("Tell Text") },\r
569   { (COLORREF)0, 0, N_("Challenge Text") },\r
570   { (COLORREF)0, 0, N_("Request Text") },\r
571   { (COLORREF)0, 0, N_("Seek Text") },\r
572   { (COLORREF)0, 0, N_("Normal Text") },\r
573   { (COLORREF)0, 0, N_("None") }\r
574 };\r
575 \r
576 \r
577 \r
578 static char *commentTitle;\r
579 static char *commentText;\r
580 static int commentIndex;\r
581 static Boolean editComment = FALSE;\r
582 \r
583 \r
584 char errorTitle[MSG_SIZ];\r
585 char errorMessage[2*MSG_SIZ];\r
586 HWND errorDialog = NULL;\r
587 BOOLEAN moveErrorMessageUp = FALSE;\r
588 BOOLEAN consoleEcho = TRUE;\r
589 CHARFORMAT consoleCF;\r
590 COLORREF consoleBackgroundColor;\r
591 \r
592 char *programVersion;\r
593 \r
594 #define CPReal 1\r
595 #define CPComm 2\r
596 #define CPSock 3\r
597 #define CPRcmd 4\r
598 typedef int CPKind;\r
599 \r
600 typedef struct {\r
601   CPKind kind;\r
602   HANDLE hProcess;\r
603   DWORD pid;\r
604   HANDLE hTo;\r
605   HANDLE hFrom;\r
606   SOCKET sock;\r
607   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
608 } ChildProc;\r
609 \r
610 #define INPUT_SOURCE_BUF_SIZE 4096\r
611 \r
612 typedef struct _InputSource {\r
613   CPKind kind;\r
614   HANDLE hFile;\r
615   SOCKET sock;\r
616   int lineByLine;\r
617   HANDLE hThread;\r
618   DWORD id;\r
619   char buf[INPUT_SOURCE_BUF_SIZE];\r
620   char *next;\r
621   DWORD count;\r
622   int error;\r
623   InputCallback func;\r
624   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
625   VOIDSTAR closure;\r
626 } InputSource;\r
627 \r
628 InputSource *consoleInputSource;\r
629 \r
630 DCB dcb;\r
631 \r
632 /* forward */\r
633 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
634 VOID ConsoleCreate();\r
635 LRESULT CALLBACK\r
636   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
637 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
638 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
639 VOID ParseCommSettings(char *arg, DCB *dcb);\r
640 LRESULT CALLBACK\r
641   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
642 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
643 void ParseIcsTextMenu(char *icsTextMenuString);\r
644 VOID PopUpMoveDialog(char firstchar);\r
645 VOID PopUpNameDialog(char firstchar);\r
646 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
647 \r
648 /* [AS] */\r
649 int NewGameFRC();\r
650 int GameListOptions();\r
651 \r
652 int dummy; // [HGM] for obsolete args\r
653 \r
654 HWND hwndMain = NULL;        /* root window*/\r
655 HWND hwndConsole = NULL;\r
656 HWND commentDialog = NULL;\r
657 HWND moveHistoryDialog = NULL;\r
658 HWND evalGraphDialog = NULL;\r
659 HWND engineOutputDialog = NULL;\r
660 HWND gameListDialog = NULL;\r
661 HWND editTagsDialog = NULL;\r
662 \r
663 int commentUp = FALSE;\r
664 \r
665 WindowPlacement wpMain;\r
666 WindowPlacement wpConsole;\r
667 WindowPlacement wpComment;\r
668 WindowPlacement wpMoveHistory;\r
669 WindowPlacement wpEvalGraph;\r
670 WindowPlacement wpEngineOutput;\r
671 WindowPlacement wpGameList;\r
672 WindowPlacement wpTags;\r
673 \r
674 VOID EngineOptionsPopup(); // [HGM] settings\r
675 \r
676 VOID GothicPopUp(char *title, VariantClass variant);\r
677 /*\r
678  * Setting "frozen" should disable all user input other than deleting\r
679  * the window.  We do this while engines are initializing themselves.\r
680  */\r
681 static int frozen = 0;\r
682 static int oldMenuItemState[MENU_BAR_ITEMS];\r
683 void FreezeUI()\r
684 {\r
685   HMENU hmenu;\r
686   int i;\r
687 \r
688   if (frozen) return;\r
689   frozen = 1;\r
690   hmenu = GetMenu(hwndMain);\r
691   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
692     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
693   }\r
694   DrawMenuBar(hwndMain);\r
695 }\r
696 \r
697 /* Undo a FreezeUI */\r
698 void ThawUI()\r
699 {\r
700   HMENU hmenu;\r
701   int i;\r
702 \r
703   if (!frozen) return;\r
704   frozen = 0;\r
705   hmenu = GetMenu(hwndMain);\r
706   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
707     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
708   }\r
709   DrawMenuBar(hwndMain);\r
710 }\r
711 \r
712 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
713 \r
714 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
715 #ifdef JAWS\r
716 #include "jaws.c"\r
717 #else\r
718 #define JAWS_INIT\r
719 #define JAWS_ARGS\r
720 #define JAWS_ALT_INTERCEPT\r
721 #define JAWS_KB_NAVIGATION\r
722 #define JAWS_MENU_ITEMS\r
723 #define JAWS_SILENCE\r
724 #define JAWS_REPLAY\r
725 #define JAWS_ACCEL\r
726 #define JAWS_COPYRIGHT\r
727 #define JAWS_DELETE(X) X\r
728 #define SAYMACHINEMOVE()\r
729 #define SAY(X)\r
730 #endif\r
731 \r
732 /*---------------------------------------------------------------------------*\\r
733  *\r
734  * WinMain\r
735  *\r
736 \*---------------------------------------------------------------------------*/\r
737 \r
738 int APIENTRY\r
739 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
740         LPSTR lpCmdLine, int nCmdShow)\r
741 {\r
742   MSG msg;\r
743   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
744 //  INITCOMMONCONTROLSEX ex;\r
745 \r
746   debugFP = stderr;\r
747 \r
748   LoadLibrary("RICHED32.DLL");\r
749   consoleCF.cbSize = sizeof(CHARFORMAT);\r
750 \r
751   if (!InitApplication(hInstance)) {\r
752     return (FALSE);\r
753   }\r
754   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
755     return (FALSE);\r
756   }\r
757 \r
758   JAWS_INIT\r
759 \r
760 //  InitCommonControlsEx(&ex);\r
761   InitCommonControls();\r
762 \r
763   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
764   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
765   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
766 \r
767   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
768 \r
769   while (GetMessage(&msg, /* message structure */\r
770                     NULL, /* handle of window receiving the message */\r
771                     0,    /* lowest message to examine */\r
772                     0))   /* highest message to examine */\r
773     {\r
774 \r
775       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
776         // [HGM] navigate: switch between all windows with tab\r
777         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
778         int i, currentElement = 0;\r
779 \r
780         // first determine what element of the chain we come from (if any)\r
781         if(appData.icsActive) {\r
782             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
783             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
784         }\r
785         if(engineOutputDialog && EngineOutputIsUp()) {\r
786             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
787             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
788         }\r
789         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
790             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
791         }\r
792         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
793         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
794         if(msg.hwnd == e1)                 currentElement = 2; else\r
795         if(msg.hwnd == e2)                 currentElement = 3; else\r
796         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
797         if(msg.hwnd == mh)                currentElement = 4; else\r
798         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
799         if(msg.hwnd == hText)  currentElement = 5; else\r
800         if(msg.hwnd == hInput) currentElement = 6; else\r
801         for (i = 0; i < N_BUTTONS; i++) {\r
802             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
803         }\r
804 \r
805         // determine where to go to\r
806         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
807           do {\r
808             currentElement = (currentElement + direction) % 7;\r
809             switch(currentElement) {\r
810                 case 0:\r
811                   h = hwndMain; break; // passing this case always makes the loop exit\r
812                 case 1:\r
813                   h = buttonDesc[0].hwnd; break; // could be NULL\r
814                 case 2:\r
815                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
816                   h = e1; break;\r
817                 case 3:\r
818                   if(!EngineOutputIsUp()) continue;\r
819                   h = e2; break;\r
820                 case 4:\r
821                   if(!MoveHistoryIsUp()) continue;\r
822                   h = mh; break;\r
823 //              case 6: // input to eval graph does not seem to get here!\r
824 //                if(!EvalGraphIsUp()) continue;\r
825 //                h = evalGraphDialog; break;\r
826                 case 5:\r
827                   if(!appData.icsActive) continue;\r
828                   SAY("display");\r
829                   h = hText; break;\r
830                 case 6:\r
831                   if(!appData.icsActive) continue;\r
832                   SAY("input");\r
833                   h = hInput; break;\r
834             }\r
835           } while(h == 0);\r
836 \r
837           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
838           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
839           SetFocus(h);\r
840 \r
841           continue; // this message now has been processed\r
842         }\r
843       }\r
844 \r
845       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
846           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
847           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
848           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
849           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
850           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
851           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
852           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
853           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
854           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
855         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
856         for(i=0; i<MAX_CHAT; i++) \r
857             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
858                 done = 1; break;\r
859         }\r
860         if(done) continue; // [HGM] chat: end patch\r
861         TranslateMessage(&msg); /* Translates virtual key codes */\r
862         DispatchMessage(&msg);  /* Dispatches message to window */\r
863       }\r
864     }\r
865 \r
866 \r
867   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
868 }\r
869 \r
870 /*---------------------------------------------------------------------------*\\r
871  *\r
872  * Initialization functions\r
873  *\r
874 \*---------------------------------------------------------------------------*/\r
875 \r
876 void\r
877 SetUserLogo()\r
878 {   // update user logo if necessary\r
879     static char oldUserName[MSG_SIZ], *curName;\r
880 \r
881     if(appData.autoLogo) {\r
882           curName = UserName();\r
883           if(strcmp(curName, oldUserName)) {\r
884             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
885                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
886                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
887           }\r
888     }\r
889 }\r
890 \r
891 BOOL\r
892 InitApplication(HINSTANCE hInstance)\r
893 {\r
894   WNDCLASS wc;\r
895 \r
896   /* Fill in window class structure with parameters that describe the */\r
897   /* main window. */\r
898 \r
899   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
900   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
901   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
902   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
903   wc.hInstance     = hInstance;         /* Owner of this class */\r
904   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
905   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
906   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
907   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
908   wc.lpszClassName = szAppName;                 /* Name to register as */\r
909 \r
910   /* Register the window class and return success/failure code. */\r
911   if (!RegisterClass(&wc)) return FALSE;\r
912 \r
913   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
914   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
915   wc.cbClsExtra    = 0;\r
916   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
917   wc.hInstance     = hInstance;\r
918   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
919   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
920   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
921   wc.lpszMenuName  = NULL;\r
922   wc.lpszClassName = szConsoleName;\r
923 \r
924   if (!RegisterClass(&wc)) return FALSE;\r
925   return TRUE;\r
926 }\r
927 \r
928 \r
929 /* Set by InitInstance, used by EnsureOnScreen */\r
930 int screenHeight, screenWidth;\r
931 \r
932 void\r
933 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
934 {\r
935 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
936   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
937   if (*x > screenWidth - 32) *x = 0;\r
938   if (*y > screenHeight - 32) *y = 0;\r
939   if (*x < minX) *x = minX;\r
940   if (*y < minY) *y = minY;\r
941 }\r
942 \r
943 BOOL\r
944 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
945 {\r
946   HWND hwnd; /* Main window handle. */\r
947   int ibs;\r
948   WINDOWPLACEMENT wp;\r
949   char *filepart;\r
950 \r
951   hInst = hInstance;    /* Store instance handle in our global variable */\r
952   programName = szAppName;\r
953 \r
954   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
955     *filepart = NULLCHAR;\r
956   } else {\r
957     GetCurrentDirectory(MSG_SIZ, installDir);\r
958   }\r
959   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
960   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
961   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
962   /* xboard, and older WinBoards, controlled the move sound with the\r
963      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
964      always turn the option on (so that the backend will call us),\r
965      then let the user turn the sound off by setting it to silence if\r
966      desired.  To accommodate old winboard.ini files saved by old\r
967      versions of WinBoard, we also turn off the sound if the option\r
968      was initially set to false. [HGM] taken out of InitAppData */\r
969   if (!appData.ringBellAfterMoves) {\r
970     sounds[(int)SoundMove].name = strdup("");\r
971     appData.ringBellAfterMoves = TRUE;\r
972   }\r
973   if (appData.debugMode) {\r
974     debugFP = fopen(appData.nameOfDebugFile, "w");\r
975     setbuf(debugFP, NULL);\r
976   }\r
977 \r
978   LoadLanguageFile(appData.language);\r
979 \r
980   InitBackEnd1();\r
981 \r
982 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
983 //  InitEngineUCI( installDir, &second );\r
984 \r
985   /* Create a main window for this application instance. */\r
986   hwnd = CreateWindow(szAppName, szTitle,\r
987                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
988                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
989                       NULL, NULL, hInstance, NULL);\r
990   hwndMain = hwnd;\r
991 \r
992   /* If window could not be created, return "failure" */\r
993   if (!hwnd) {\r
994     return (FALSE);\r
995   }\r
996 \r
997   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
998   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
999       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1000 \r
1001       if (first.programLogo == NULL && appData.debugMode) {\r
1002           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1003       }\r
1004   } else if(appData.autoLogo) {\r
1005       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1006         char buf[MSG_SIZ];\r
1007           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1008         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1009       }\r
1010   }\r
1011 \r
1012   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1013       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1014 \r
1015       if (second.programLogo == NULL && appData.debugMode) {\r
1016           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1017       }\r
1018   } else if(appData.autoLogo) {\r
1019       char buf[MSG_SIZ];\r
1020       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1021         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1022         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1023       } else\r
1024       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1025         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1026         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1027       }\r
1028   }\r
1029 \r
1030   SetUserLogo();\r
1031 \r
1032   iconWhite = LoadIcon(hInstance, "icon_white");\r
1033   iconBlack = LoadIcon(hInstance, "icon_black");\r
1034   iconCurrent = iconWhite;\r
1035   InitDrawingColors();\r
1036   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1037   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1038   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1039     /* Compute window size for each board size, and use the largest\r
1040        size that fits on this screen as the default. */\r
1041     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1042     if (boardSize == (BoardSize)-1 &&\r
1043         winH <= screenHeight\r
1044            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1045         && winW <= screenWidth) {\r
1046       boardSize = (BoardSize)ibs;\r
1047     }\r
1048   }\r
1049 \r
1050   InitDrawingSizes(boardSize, 0);\r
1051   TranslateMenus(1);\r
1052   InitMenuChecks();\r
1053   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1054 \r
1055   /* [AS] Load textures if specified */\r
1056   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1057   \r
1058   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1059       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1060       liteBackTextureMode = appData.liteBackTextureMode;\r
1061 \r
1062       if (liteBackTexture == NULL && appData.debugMode) {\r
1063           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1064       }\r
1065   }\r
1066   \r
1067   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1068       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1069       darkBackTextureMode = appData.darkBackTextureMode;\r
1070 \r
1071       if (darkBackTexture == NULL && appData.debugMode) {\r
1072           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1073       }\r
1074   }\r
1075 \r
1076   mysrandom( (unsigned) time(NULL) );\r
1077 \r
1078   /* [AS] Restore layout */\r
1079   if( wpMoveHistory.visible ) {\r
1080       MoveHistoryPopUp();\r
1081   }\r
1082 \r
1083   if( wpEvalGraph.visible ) {\r
1084       EvalGraphPopUp();\r
1085   }\r
1086 \r
1087   if( wpEngineOutput.visible ) {\r
1088       EngineOutputPopUp();\r
1089   }\r
1090 \r
1091   /* Make the window visible; update its client area; and return "success" */\r
1092   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1093   wp.length = sizeof(WINDOWPLACEMENT);\r
1094   wp.flags = 0;\r
1095   wp.showCmd = nCmdShow;\r
1096   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1097   wp.rcNormalPosition.left = wpMain.x;\r
1098   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1099   wp.rcNormalPosition.top = wpMain.y;\r
1100   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1101   SetWindowPlacement(hwndMain, &wp);\r
1102 \r
1103   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1104 \r
1105   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1106                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1107 \r
1108   if (hwndConsole) {\r
1109 #if AOT_CONSOLE\r
1110     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1111                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1112 #endif\r
1113     ShowWindow(hwndConsole, nCmdShow);\r
1114     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1115       char buf[MSG_SIZ], *p = buf, *q;\r
1116         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1117       do {\r
1118         q = strchr(p, ';');\r
1119         if(q) *q++ = 0;\r
1120         if(*p) ChatPopUp(p);\r
1121       } while(p=q);\r
1122     }\r
1123     SetActiveWindow(hwndConsole);\r
1124   }\r
1125   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1126   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1127 \r
1128   return TRUE;\r
1129 \r
1130 }\r
1131 \r
1132 VOID\r
1133 InitMenuChecks()\r
1134 {\r
1135   HMENU hmenu = GetMenu(hwndMain);\r
1136 \r
1137   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1138                         MF_BYCOMMAND|((appData.icsActive &&\r
1139                                        *appData.icsCommPort != NULLCHAR) ?\r
1140                                       MF_ENABLED : MF_GRAYED));\r
1141   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1142                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1143                                      MF_CHECKED : MF_UNCHECKED));\r
1144 }\r
1145 \r
1146 //---------------------------------------------------------------------------------------------------------\r
1147 \r
1148 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1149 #define XBOARD FALSE\r
1150 \r
1151 #define OPTCHAR "/"\r
1152 #define SEPCHAR "="\r
1153 \r
1154 #include "args.h"\r
1155 \r
1156 // front-end part of option handling\r
1157 \r
1158 VOID\r
1159 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1160 {\r
1161   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1162   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1163   DeleteDC(hdc);\r
1164   lf->lfWidth = 0;\r
1165   lf->lfEscapement = 0;\r
1166   lf->lfOrientation = 0;\r
1167   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1168   lf->lfItalic = mfp->italic;\r
1169   lf->lfUnderline = mfp->underline;\r
1170   lf->lfStrikeOut = mfp->strikeout;\r
1171   lf->lfCharSet = mfp->charset;\r
1172   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1173   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1174   lf->lfQuality = DEFAULT_QUALITY;\r
1175   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1176     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1177 }\r
1178 \r
1179 void\r
1180 CreateFontInMF(MyFont *mf)\r
1181\r
1182   LFfromMFP(&mf->lf, &mf->mfp);\r
1183   if (mf->hf) DeleteObject(mf->hf);\r
1184   mf->hf = CreateFontIndirect(&mf->lf);\r
1185 }\r
1186 \r
1187 // [HGM] This platform-dependent table provides the location for storing the color info\r
1188 void *\r
1189 colorVariable[] = {\r
1190   &whitePieceColor, \r
1191   &blackPieceColor, \r
1192   &lightSquareColor,\r
1193   &darkSquareColor, \r
1194   &highlightSquareColor,\r
1195   &premoveHighlightColor,\r
1196   NULL,\r
1197   &consoleBackgroundColor,\r
1198   &appData.fontForeColorWhite,\r
1199   &appData.fontBackColorWhite,\r
1200   &appData.fontForeColorBlack,\r
1201   &appData.fontBackColorBlack,\r
1202   &appData.evalHistColorWhite,\r
1203   &appData.evalHistColorBlack,\r
1204   &appData.highlightArrowColor,\r
1205 };\r
1206 \r
1207 /* Command line font name parser.  NULL name means do nothing.\r
1208    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1209    For backward compatibility, syntax without the colon is also\r
1210    accepted, but font names with digits in them won't work in that case.\r
1211 */\r
1212 VOID\r
1213 ParseFontName(char *name, MyFontParams *mfp)\r
1214 {\r
1215   char *p, *q;\r
1216   if (name == NULL) return;\r
1217   p = name;\r
1218   q = strchr(p, ':');\r
1219   if (q) {\r
1220     if (q - p >= sizeof(mfp->faceName))\r
1221       ExitArgError(_("Font name too long:"), name);\r
1222     memcpy(mfp->faceName, p, q - p);\r
1223     mfp->faceName[q - p] = NULLCHAR;\r
1224     p = q + 1;\r
1225   } else {\r
1226     q = mfp->faceName;\r
1227     while (*p && !isdigit(*p)) {\r
1228       *q++ = *p++;\r
1229       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1230         ExitArgError(_("Font name too long:"), name);\r
1231     }\r
1232     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1233     *q = NULLCHAR;\r
1234   }\r
1235   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1236   mfp->pointSize = (float) atof(p);\r
1237   mfp->bold = (strchr(p, 'b') != NULL);\r
1238   mfp->italic = (strchr(p, 'i') != NULL);\r
1239   mfp->underline = (strchr(p, 'u') != NULL);\r
1240   mfp->strikeout = (strchr(p, 's') != NULL);\r
1241   mfp->charset = DEFAULT_CHARSET;\r
1242   q = strchr(p, 'c');\r
1243   if (q)\r
1244     mfp->charset = (BYTE) atoi(q+1);\r
1245 }\r
1246 \r
1247 void\r
1248 ParseFont(char *name, int number)\r
1249 { // wrapper to shield back-end from 'font'\r
1250   ParseFontName(name, &font[boardSize][number]->mfp);\r
1251 }\r
1252 \r
1253 void\r
1254 SetFontDefaults()\r
1255 { // in WB  we have a 2D array of fonts; this initializes their description\r
1256   int i, j;\r
1257   /* Point font array elements to structures and\r
1258      parse default font names */\r
1259   for (i=0; i<NUM_FONTS; i++) {\r
1260     for (j=0; j<NUM_SIZES; j++) {\r
1261       font[j][i] = &fontRec[j][i];\r
1262       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1263     }\r
1264   }\r
1265 }\r
1266 \r
1267 void\r
1268 CreateFonts()\r
1269 { // here we create the actual fonts from the selected descriptions\r
1270   int i, j;\r
1271   for (i=0; i<NUM_FONTS; i++) {\r
1272     for (j=0; j<NUM_SIZES; j++) {\r
1273       CreateFontInMF(font[j][i]);\r
1274     }\r
1275   }\r
1276 }\r
1277 /* Color name parser.\r
1278    X version accepts X color names, but this one\r
1279    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1280 COLORREF\r
1281 ParseColorName(char *name)\r
1282 {\r
1283   int red, green, blue, count;\r
1284   char buf[MSG_SIZ];\r
1285 \r
1286   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1287   if (count != 3) {\r
1288     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1289       &red, &green, &blue);\r
1290   }\r
1291   if (count != 3) {\r
1292     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1293     DisplayError(buf, 0);\r
1294     return RGB(0, 0, 0);\r
1295   }\r
1296   return PALETTERGB(red, green, blue);\r
1297 }\r
1298 \r
1299 void\r
1300 ParseColor(int n, char *name)\r
1301 { // for WinBoard the color is an int, which needs to be derived from the string\r
1302   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1303 }\r
1304 \r
1305 void\r
1306 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1307 {\r
1308   char *e = argValue;\r
1309   int eff = 0;\r
1310 \r
1311   while (*e) {\r
1312     if (*e == 'b')      eff |= CFE_BOLD;\r
1313     else if (*e == 'i') eff |= CFE_ITALIC;\r
1314     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1315     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1316     else if (*e == '#' || isdigit(*e)) break;\r
1317     e++;\r
1318   }\r
1319   *effects = eff;\r
1320   *color   = ParseColorName(e);\r
1321 }\r
1322 \r
1323 void\r
1324 ParseTextAttribs(ColorClass cc, char *s)\r
1325 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1326     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1327     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1328 }\r
1329 \r
1330 void\r
1331 ParseBoardSize(void *addr, char *name)\r
1332 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1333   BoardSize bs = SizeTiny;\r
1334   while (sizeInfo[bs].name != NULL) {\r
1335     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1336         *(BoardSize *)addr = bs;\r
1337         return;\r
1338     }\r
1339     bs++;\r
1340   }\r
1341   ExitArgError(_("Unrecognized board size value"), name);\r
1342 }\r
1343 \r
1344 void\r
1345 LoadAllSounds()\r
1346 { // [HGM] import name from appData first\r
1347   ColorClass cc;\r
1348   SoundClass sc;\r
1349   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1350     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1351     textAttribs[cc].sound.data = NULL;\r
1352     MyLoadSound(&textAttribs[cc].sound);\r
1353   }\r
1354   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1355     textAttribs[cc].sound.name = strdup("");\r
1356     textAttribs[cc].sound.data = NULL;\r
1357   }\r
1358   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1359     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1360     sounds[sc].data = NULL;\r
1361     MyLoadSound(&sounds[sc]);\r
1362   }\r
1363 }\r
1364 \r
1365 void\r
1366 SetCommPortDefaults()\r
1367 {\r
1368    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1369   dcb.DCBlength = sizeof(DCB);\r
1370   dcb.BaudRate = 9600;\r
1371   dcb.fBinary = TRUE;\r
1372   dcb.fParity = FALSE;\r
1373   dcb.fOutxCtsFlow = FALSE;\r
1374   dcb.fOutxDsrFlow = FALSE;\r
1375   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1376   dcb.fDsrSensitivity = FALSE;\r
1377   dcb.fTXContinueOnXoff = TRUE;\r
1378   dcb.fOutX = FALSE;\r
1379   dcb.fInX = FALSE;\r
1380   dcb.fNull = FALSE;\r
1381   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1382   dcb.fAbortOnError = FALSE;\r
1383   dcb.ByteSize = 7;\r
1384   dcb.Parity = SPACEPARITY;\r
1385   dcb.StopBits = ONESTOPBIT;\r
1386 }\r
1387 \r
1388 // [HGM] args: these three cases taken out to stay in front-end\r
1389 void\r
1390 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1391 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1392         // while the curent board size determines the element. This system should be ported to XBoard.\r
1393         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1394         int bs;\r
1395         for (bs=0; bs<NUM_SIZES; bs++) {\r
1396           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1397           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1398           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1399             ad->argName, mfp->faceName, mfp->pointSize,\r
1400             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1401             mfp->bold ? "b" : "",\r
1402             mfp->italic ? "i" : "",\r
1403             mfp->underline ? "u" : "",\r
1404             mfp->strikeout ? "s" : "",\r
1405             (int)mfp->charset);\r
1406         }\r
1407       }\r
1408 \r
1409 void\r
1410 ExportSounds()\r
1411 { // [HGM] copy the names from the internal WB variables to appData\r
1412   ColorClass cc;\r
1413   SoundClass sc;\r
1414   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1415     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1416   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1417     (&appData.soundMove)[sc] = sounds[sc].name;\r
1418 }\r
1419 \r
1420 void\r
1421 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1422 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1423         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1424         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1425           (ta->effects & CFE_BOLD) ? "b" : "",\r
1426           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1427           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1428           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1429           (ta->effects) ? " " : "",\r
1430           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1431       }\r
1432 \r
1433 void\r
1434 SaveColor(FILE *f, ArgDescriptor *ad)\r
1435 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1436         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1437         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1438           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1439 }\r
1440 \r
1441 void\r
1442 SaveBoardSize(FILE *f, char *name, void *addr)\r
1443 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1444   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1445 }\r
1446 \r
1447 void\r
1448 ParseCommPortSettings(char *s)\r
1449 { // wrapper to keep dcb from back-end\r
1450   ParseCommSettings(s, &dcb);\r
1451 }\r
1452 \r
1453 void\r
1454 GetWindowCoords()\r
1455 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1456   GetActualPlacement(hwndMain, &wpMain);\r
1457   GetActualPlacement(hwndConsole, &wpConsole);\r
1458   GetActualPlacement(commentDialog, &wpComment);\r
1459   GetActualPlacement(editTagsDialog, &wpTags);\r
1460   GetActualPlacement(gameListDialog, &wpGameList);\r
1461   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1462   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1463   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1464 }\r
1465 \r
1466 void\r
1467 PrintCommPortSettings(FILE *f, char *name)\r
1468 { // wrapper to shield back-end from DCB\r
1469       PrintCommSettings(f, name, &dcb);\r
1470 }\r
1471 \r
1472 int\r
1473 MySearchPath(char *installDir, char *name, char *fullname)\r
1474 {\r
1475   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1476   if(name[0]== '%') {\r
1477     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1478     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1479       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1480       *strchr(buf, '%') = 0;\r
1481       strcat(fullname, getenv(buf));\r
1482       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1483     }\r
1484     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1485     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1486     return (int) strlen(fullname);\r
1487   }\r
1488   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1489 }\r
1490 \r
1491 int\r
1492 MyGetFullPathName(char *name, char *fullname)\r
1493 {\r
1494   char *dummy;\r
1495   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1496 }\r
1497 \r
1498 int\r
1499 MainWindowUp()\r
1500 { // [HGM] args: allows testing if main window is realized from back-end\r
1501   return hwndMain != NULL;\r
1502 }\r
1503 \r
1504 void\r
1505 PopUpStartupDialog()\r
1506 {\r
1507     FARPROC lpProc;\r
1508     \r
1509     LoadLanguageFile(appData.language);\r
1510     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1511     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1512     FreeProcInstance(lpProc);\r
1513 }\r
1514 \r
1515 /*---------------------------------------------------------------------------*\\r
1516  *\r
1517  * GDI board drawing routines\r
1518  *\r
1519 \*---------------------------------------------------------------------------*/\r
1520 \r
1521 /* [AS] Draw square using background texture */\r
1522 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1523 {\r
1524     XFORM   x;\r
1525 \r
1526     if( mode == 0 ) {\r
1527         return; /* Should never happen! */\r
1528     }\r
1529 \r
1530     SetGraphicsMode( dst, GM_ADVANCED );\r
1531 \r
1532     switch( mode ) {\r
1533     case 1:\r
1534         /* Identity */\r
1535         break;\r
1536     case 2:\r
1537         /* X reflection */\r
1538         x.eM11 = -1.0;\r
1539         x.eM12 = 0;\r
1540         x.eM21 = 0;\r
1541         x.eM22 = 1.0;\r
1542         x.eDx = (FLOAT) dw + dx - 1;\r
1543         x.eDy = 0;\r
1544         dx = 0;\r
1545         SetWorldTransform( dst, &x );\r
1546         break;\r
1547     case 3:\r
1548         /* Y reflection */\r
1549         x.eM11 = 1.0;\r
1550         x.eM12 = 0;\r
1551         x.eM21 = 0;\r
1552         x.eM22 = -1.0;\r
1553         x.eDx = 0;\r
1554         x.eDy = (FLOAT) dh + dy - 1;\r
1555         dy = 0;\r
1556         SetWorldTransform( dst, &x );\r
1557         break;\r
1558     case 4:\r
1559         /* X/Y flip */\r
1560         x.eM11 = 0;\r
1561         x.eM12 = 1.0;\r
1562         x.eM21 = 1.0;\r
1563         x.eM22 = 0;\r
1564         x.eDx = (FLOAT) dx;\r
1565         x.eDy = (FLOAT) dy;\r
1566         dx = 0;\r
1567         dy = 0;\r
1568         SetWorldTransform( dst, &x );\r
1569         break;\r
1570     }\r
1571 \r
1572     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1573 \r
1574     x.eM11 = 1.0;\r
1575     x.eM12 = 0;\r
1576     x.eM21 = 0;\r
1577     x.eM22 = 1.0;\r
1578     x.eDx = 0;\r
1579     x.eDy = 0;\r
1580     SetWorldTransform( dst, &x );\r
1581 \r
1582     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1583 }\r
1584 \r
1585 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1586 enum {\r
1587     PM_WP = (int) WhitePawn, \r
1588     PM_WN = (int) WhiteKnight, \r
1589     PM_WB = (int) WhiteBishop, \r
1590     PM_WR = (int) WhiteRook, \r
1591     PM_WQ = (int) WhiteQueen, \r
1592     PM_WF = (int) WhiteFerz, \r
1593     PM_WW = (int) WhiteWazir, \r
1594     PM_WE = (int) WhiteAlfil, \r
1595     PM_WM = (int) WhiteMan, \r
1596     PM_WO = (int) WhiteCannon, \r
1597     PM_WU = (int) WhiteUnicorn, \r
1598     PM_WH = (int) WhiteNightrider, \r
1599     PM_WA = (int) WhiteAngel, \r
1600     PM_WC = (int) WhiteMarshall, \r
1601     PM_WAB = (int) WhiteCardinal, \r
1602     PM_WD = (int) WhiteDragon, \r
1603     PM_WL = (int) WhiteLance, \r
1604     PM_WS = (int) WhiteCobra, \r
1605     PM_WV = (int) WhiteFalcon, \r
1606     PM_WSG = (int) WhiteSilver, \r
1607     PM_WG = (int) WhiteGrasshopper, \r
1608     PM_WK = (int) WhiteKing,\r
1609     PM_BP = (int) BlackPawn, \r
1610     PM_BN = (int) BlackKnight, \r
1611     PM_BB = (int) BlackBishop, \r
1612     PM_BR = (int) BlackRook, \r
1613     PM_BQ = (int) BlackQueen, \r
1614     PM_BF = (int) BlackFerz, \r
1615     PM_BW = (int) BlackWazir, \r
1616     PM_BE = (int) BlackAlfil, \r
1617     PM_BM = (int) BlackMan,\r
1618     PM_BO = (int) BlackCannon, \r
1619     PM_BU = (int) BlackUnicorn, \r
1620     PM_BH = (int) BlackNightrider, \r
1621     PM_BA = (int) BlackAngel, \r
1622     PM_BC = (int) BlackMarshall, \r
1623     PM_BG = (int) BlackGrasshopper, \r
1624     PM_BAB = (int) BlackCardinal,\r
1625     PM_BD = (int) BlackDragon,\r
1626     PM_BL = (int) BlackLance,\r
1627     PM_BS = (int) BlackCobra,\r
1628     PM_BV = (int) BlackFalcon,\r
1629     PM_BSG = (int) BlackSilver,\r
1630     PM_BK = (int) BlackKing\r
1631 };\r
1632 \r
1633 static HFONT hPieceFont = NULL;\r
1634 static HBITMAP hPieceMask[(int) EmptySquare];\r
1635 static HBITMAP hPieceFace[(int) EmptySquare];\r
1636 static int fontBitmapSquareSize = 0;\r
1637 static char pieceToFontChar[(int) EmptySquare] =\r
1638                               { 'p', 'n', 'b', 'r', 'q', \r
1639                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1640                       'k', 'o', 'm', 'v', 't', 'w', \r
1641                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1642                                                               'l' };\r
1643 \r
1644 extern BOOL SetCharTable( char *table, const char * map );\r
1645 /* [HGM] moved to backend.c */\r
1646 \r
1647 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1648 {\r
1649     HBRUSH hbrush;\r
1650     BYTE r1 = GetRValue( color );\r
1651     BYTE g1 = GetGValue( color );\r
1652     BYTE b1 = GetBValue( color );\r
1653     BYTE r2 = r1 / 2;\r
1654     BYTE g2 = g1 / 2;\r
1655     BYTE b2 = b1 / 2;\r
1656     RECT rc;\r
1657 \r
1658     /* Create a uniform background first */\r
1659     hbrush = CreateSolidBrush( color );\r
1660     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1661     FillRect( hdc, &rc, hbrush );\r
1662     DeleteObject( hbrush );\r
1663     \r
1664     if( mode == 1 ) {\r
1665         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1666         int steps = squareSize / 2;\r
1667         int i;\r
1668 \r
1669         for( i=0; i<steps; i++ ) {\r
1670             BYTE r = r1 - (r1-r2) * i / steps;\r
1671             BYTE g = g1 - (g1-g2) * i / steps;\r
1672             BYTE b = b1 - (b1-b2) * i / steps;\r
1673 \r
1674             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1675             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1676             FillRect( hdc, &rc, hbrush );\r
1677             DeleteObject(hbrush);\r
1678         }\r
1679     }\r
1680     else if( mode == 2 ) {\r
1681         /* Diagonal gradient, good more or less for every piece */\r
1682         POINT triangle[3];\r
1683         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1684         HBRUSH hbrush_old;\r
1685         int steps = squareSize;\r
1686         int i;\r
1687 \r
1688         triangle[0].x = squareSize - steps;\r
1689         triangle[0].y = squareSize;\r
1690         triangle[1].x = squareSize;\r
1691         triangle[1].y = squareSize;\r
1692         triangle[2].x = squareSize;\r
1693         triangle[2].y = squareSize - steps;\r
1694 \r
1695         for( i=0; i<steps; i++ ) {\r
1696             BYTE r = r1 - (r1-r2) * i / steps;\r
1697             BYTE g = g1 - (g1-g2) * i / steps;\r
1698             BYTE b = b1 - (b1-b2) * i / steps;\r
1699 \r
1700             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1701             hbrush_old = SelectObject( hdc, hbrush );\r
1702             Polygon( hdc, triangle, 3 );\r
1703             SelectObject( hdc, hbrush_old );\r
1704             DeleteObject(hbrush);\r
1705             triangle[0].x++;\r
1706             triangle[2].y++;\r
1707         }\r
1708 \r
1709         SelectObject( hdc, hpen );\r
1710     }\r
1711 }\r
1712 \r
1713 /*\r
1714     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1715     seems to work ok. The main problem here is to find the "inside" of a chess\r
1716     piece: follow the steps as explained below.\r
1717 */\r
1718 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1719 {\r
1720     HBITMAP hbm;\r
1721     HBITMAP hbm_old;\r
1722     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1723     RECT rc;\r
1724     SIZE sz;\r
1725     POINT pt;\r
1726     int backColor = whitePieceColor; \r
1727     int foreColor = blackPieceColor;\r
1728     \r
1729     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1730         backColor = appData.fontBackColorWhite;\r
1731         foreColor = appData.fontForeColorWhite;\r
1732     }\r
1733     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1734         backColor = appData.fontBackColorBlack;\r
1735         foreColor = appData.fontForeColorBlack;\r
1736     }\r
1737 \r
1738     /* Mask */\r
1739     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1740 \r
1741     hbm_old = SelectObject( hdc, hbm );\r
1742 \r
1743     rc.left = 0;\r
1744     rc.top = 0;\r
1745     rc.right = squareSize;\r
1746     rc.bottom = squareSize;\r
1747 \r
1748     /* Step 1: background is now black */\r
1749     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1750 \r
1751     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1752 \r
1753     pt.x = (squareSize - sz.cx) / 2;\r
1754     pt.y = (squareSize - sz.cy) / 2;\r
1755 \r
1756     SetBkMode( hdc, TRANSPARENT );\r
1757     SetTextColor( hdc, chroma );\r
1758     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1759     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1760 \r
1761     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1762     /* Step 3: the area outside the piece is filled with white */\r
1763 //    FloodFill( hdc, 0, 0, chroma );\r
1764     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1765     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1766     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1767     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1768     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1769     /* \r
1770         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1771         but if the start point is not inside the piece we're lost!\r
1772         There should be a better way to do this... if we could create a region or path\r
1773         from the fill operation we would be fine for example.\r
1774     */\r
1775 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1776     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1777 \r
1778     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1779         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1780         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1781 \r
1782         SelectObject( dc2, bm2 );\r
1783         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1784         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1785         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1786         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1787         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1788 \r
1789         DeleteDC( dc2 );\r
1790         DeleteObject( bm2 );\r
1791     }\r
1792 \r
1793     SetTextColor( hdc, 0 );\r
1794     /* \r
1795         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1796         draw the piece again in black for safety.\r
1797     */\r
1798     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1799 \r
1800     SelectObject( hdc, hbm_old );\r
1801 \r
1802     if( hPieceMask[index] != NULL ) {\r
1803         DeleteObject( hPieceMask[index] );\r
1804     }\r
1805 \r
1806     hPieceMask[index] = hbm;\r
1807 \r
1808     /* Face */\r
1809     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1810 \r
1811     SelectObject( hdc, hbm );\r
1812 \r
1813     {\r
1814         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1815         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1816         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1817 \r
1818         SelectObject( dc1, hPieceMask[index] );\r
1819         SelectObject( dc2, bm2 );\r
1820         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1821         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1822         \r
1823         /* \r
1824             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1825             the piece background and deletes (makes transparent) the rest.\r
1826             Thanks to that mask, we are free to paint the background with the greates\r
1827             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1828             We use this, to make gradients and give the pieces a "roundish" look.\r
1829         */\r
1830         SetPieceBackground( hdc, backColor, 2 );\r
1831         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1832 \r
1833         DeleteDC( dc2 );\r
1834         DeleteDC( dc1 );\r
1835         DeleteObject( bm2 );\r
1836     }\r
1837 \r
1838     SetTextColor( hdc, foreColor );\r
1839     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1840 \r
1841     SelectObject( hdc, hbm_old );\r
1842 \r
1843     if( hPieceFace[index] != NULL ) {\r
1844         DeleteObject( hPieceFace[index] );\r
1845     }\r
1846 \r
1847     hPieceFace[index] = hbm;\r
1848 }\r
1849 \r
1850 static int TranslatePieceToFontPiece( int piece )\r
1851 {\r
1852     switch( piece ) {\r
1853     case BlackPawn:\r
1854         return PM_BP;\r
1855     case BlackKnight:\r
1856         return PM_BN;\r
1857     case BlackBishop:\r
1858         return PM_BB;\r
1859     case BlackRook:\r
1860         return PM_BR;\r
1861     case BlackQueen:\r
1862         return PM_BQ;\r
1863     case BlackKing:\r
1864         return PM_BK;\r
1865     case WhitePawn:\r
1866         return PM_WP;\r
1867     case WhiteKnight:\r
1868         return PM_WN;\r
1869     case WhiteBishop:\r
1870         return PM_WB;\r
1871     case WhiteRook:\r
1872         return PM_WR;\r
1873     case WhiteQueen:\r
1874         return PM_WQ;\r
1875     case WhiteKing:\r
1876         return PM_WK;\r
1877 \r
1878     case BlackAngel:\r
1879         return PM_BA;\r
1880     case BlackMarshall:\r
1881         return PM_BC;\r
1882     case BlackFerz:\r
1883         return PM_BF;\r
1884     case BlackNightrider:\r
1885         return PM_BH;\r
1886     case BlackAlfil:\r
1887         return PM_BE;\r
1888     case BlackWazir:\r
1889         return PM_BW;\r
1890     case BlackUnicorn:\r
1891         return PM_BU;\r
1892     case BlackCannon:\r
1893         return PM_BO;\r
1894     case BlackGrasshopper:\r
1895         return PM_BG;\r
1896     case BlackMan:\r
1897         return PM_BM;\r
1898     case BlackSilver:\r
1899         return PM_BSG;\r
1900     case BlackLance:\r
1901         return PM_BL;\r
1902     case BlackFalcon:\r
1903         return PM_BV;\r
1904     case BlackCobra:\r
1905         return PM_BS;\r
1906     case BlackCardinal:\r
1907         return PM_BAB;\r
1908     case BlackDragon:\r
1909         return PM_BD;\r
1910 \r
1911     case WhiteAngel:\r
1912         return PM_WA;\r
1913     case WhiteMarshall:\r
1914         return PM_WC;\r
1915     case WhiteFerz:\r
1916         return PM_WF;\r
1917     case WhiteNightrider:\r
1918         return PM_WH;\r
1919     case WhiteAlfil:\r
1920         return PM_WE;\r
1921     case WhiteWazir:\r
1922         return PM_WW;\r
1923     case WhiteUnicorn:\r
1924         return PM_WU;\r
1925     case WhiteCannon:\r
1926         return PM_WO;\r
1927     case WhiteGrasshopper:\r
1928         return PM_WG;\r
1929     case WhiteMan:\r
1930         return PM_WM;\r
1931     case WhiteSilver:\r
1932         return PM_WSG;\r
1933     case WhiteLance:\r
1934         return PM_WL;\r
1935     case WhiteFalcon:\r
1936         return PM_WV;\r
1937     case WhiteCobra:\r
1938         return PM_WS;\r
1939     case WhiteCardinal:\r
1940         return PM_WAB;\r
1941     case WhiteDragon:\r
1942         return PM_WD;\r
1943     }\r
1944 \r
1945     return 0;\r
1946 }\r
1947 \r
1948 void CreatePiecesFromFont()\r
1949 {\r
1950     LOGFONT lf;\r
1951     HDC hdc_window = NULL;\r
1952     HDC hdc = NULL;\r
1953     HFONT hfont_old;\r
1954     int fontHeight;\r
1955     int i;\r
1956 \r
1957     if( fontBitmapSquareSize < 0 ) {\r
1958         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1959         return;\r
1960     }\r
1961 \r
1962     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1963         fontBitmapSquareSize = -1;\r
1964         return;\r
1965     }\r
1966 \r
1967     if( fontBitmapSquareSize != squareSize ) {\r
1968         hdc_window = GetDC( hwndMain );\r
1969         hdc = CreateCompatibleDC( hdc_window );\r
1970 \r
1971         if( hPieceFont != NULL ) {\r
1972             DeleteObject( hPieceFont );\r
1973         }\r
1974         else {\r
1975             for( i=0; i<=(int)BlackKing; i++ ) {\r
1976                 hPieceMask[i] = NULL;\r
1977                 hPieceFace[i] = NULL;\r
1978             }\r
1979         }\r
1980 \r
1981         fontHeight = 75;\r
1982 \r
1983         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1984             fontHeight = appData.fontPieceSize;\r
1985         }\r
1986 \r
1987         fontHeight = (fontHeight * squareSize) / 100;\r
1988 \r
1989         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1990         lf.lfWidth = 0;\r
1991         lf.lfEscapement = 0;\r
1992         lf.lfOrientation = 0;\r
1993         lf.lfWeight = FW_NORMAL;\r
1994         lf.lfItalic = 0;\r
1995         lf.lfUnderline = 0;\r
1996         lf.lfStrikeOut = 0;\r
1997         lf.lfCharSet = DEFAULT_CHARSET;\r
1998         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1999         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2000         lf.lfQuality = PROOF_QUALITY;\r
2001         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2002         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2003         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2004 \r
2005         hPieceFont = CreateFontIndirect( &lf );\r
2006 \r
2007         if( hPieceFont == NULL ) {\r
2008             fontBitmapSquareSize = -2;\r
2009         }\r
2010         else {\r
2011             /* Setup font-to-piece character table */\r
2012             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2013                 /* No (or wrong) global settings, try to detect the font */\r
2014                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2015                     /* Alpha */\r
2016                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2017                 }\r
2018                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2019                     /* DiagramTT* family */\r
2020                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2021                 }\r
2022                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2023                     /* Fairy symbols */\r
2024                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2025                 }\r
2026                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2027                     /* Good Companion (Some characters get warped as literal :-( */\r
2028                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2029                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2030                     SetCharTable(pieceToFontChar, s);\r
2031                 }\r
2032                 else {\r
2033                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2034                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2035                 }\r
2036             }\r
2037 \r
2038             /* Create bitmaps */\r
2039             hfont_old = SelectObject( hdc, hPieceFont );\r
2040             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2041                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2042                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2043 \r
2044             SelectObject( hdc, hfont_old );\r
2045 \r
2046             fontBitmapSquareSize = squareSize;\r
2047         }\r
2048     }\r
2049 \r
2050     if( hdc != NULL ) {\r
2051         DeleteDC( hdc );\r
2052     }\r
2053 \r
2054     if( hdc_window != NULL ) {\r
2055         ReleaseDC( hwndMain, hdc_window );\r
2056     }\r
2057 }\r
2058 \r
2059 HBITMAP\r
2060 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2061 {\r
2062   char name[128];\r
2063 \r
2064     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2065   if (gameInfo.event &&\r
2066       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2067       strcmp(name, "k80s") == 0) {\r
2068     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2069   }\r
2070   return LoadBitmap(hinst, name);\r
2071 }\r
2072 \r
2073 \r
2074 /* Insert a color into the program's logical palette\r
2075    structure.  This code assumes the given color is\r
2076    the result of the RGB or PALETTERGB macro, and it\r
2077    knows how those macros work (which is documented).\r
2078 */\r
2079 VOID\r
2080 InsertInPalette(COLORREF color)\r
2081 {\r
2082   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2083 \r
2084   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2085     DisplayFatalError(_("Too many colors"), 0, 1);\r
2086     pLogPal->palNumEntries--;\r
2087     return;\r
2088   }\r
2089 \r
2090   pe->peFlags = (char) 0;\r
2091   pe->peRed = (char) (0xFF & color);\r
2092   pe->peGreen = (char) (0xFF & (color >> 8));\r
2093   pe->peBlue = (char) (0xFF & (color >> 16));\r
2094   return;\r
2095 }\r
2096 \r
2097 \r
2098 VOID\r
2099 InitDrawingColors()\r
2100 {\r
2101   if (pLogPal == NULL) {\r
2102     /* Allocate enough memory for a logical palette with\r
2103      * PALETTESIZE entries and set the size and version fields\r
2104      * of the logical palette structure.\r
2105      */\r
2106     pLogPal = (NPLOGPALETTE)\r
2107       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2108                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2109     pLogPal->palVersion    = 0x300;\r
2110   }\r
2111   pLogPal->palNumEntries = 0;\r
2112 \r
2113   InsertInPalette(lightSquareColor);\r
2114   InsertInPalette(darkSquareColor);\r
2115   InsertInPalette(whitePieceColor);\r
2116   InsertInPalette(blackPieceColor);\r
2117   InsertInPalette(highlightSquareColor);\r
2118   InsertInPalette(premoveHighlightColor);\r
2119 \r
2120   /*  create a logical color palette according the information\r
2121    *  in the LOGPALETTE structure.\r
2122    */\r
2123   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2124 \r
2125   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2126   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2127   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2128   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2129   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2130   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2131   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2132   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2133   /* [AS] Force rendering of the font-based pieces */\r
2134   if( fontBitmapSquareSize > 0 ) {\r
2135     fontBitmapSquareSize = 0;\r
2136   }\r
2137 }\r
2138 \r
2139 \r
2140 int\r
2141 BoardWidth(int boardSize, int n)\r
2142 { /* [HGM] argument n added to allow different width and height */\r
2143   int lineGap = sizeInfo[boardSize].lineGap;\r
2144 \r
2145   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2146       lineGap = appData.overrideLineGap;\r
2147   }\r
2148 \r
2149   return (n + 1) * lineGap +\r
2150           n * sizeInfo[boardSize].squareSize;\r
2151 }\r
2152 \r
2153 /* Respond to board resize by dragging edge */\r
2154 VOID\r
2155 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2156 {\r
2157   BoardSize newSize = NUM_SIZES - 1;\r
2158   static int recurse = 0;\r
2159   if (IsIconic(hwndMain)) return;\r
2160   if (recurse > 0) return;\r
2161   recurse++;\r
2162   while (newSize > 0) {\r
2163         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2164         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2165            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2166     newSize--;\r
2167   } \r
2168   boardSize = newSize;\r
2169   InitDrawingSizes(boardSize, flags);\r
2170   recurse--;\r
2171 }\r
2172 \r
2173 \r
2174 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2175 \r
2176 VOID\r
2177 InitDrawingSizes(BoardSize boardSize, int flags)\r
2178 {\r
2179   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2180   ChessSquare piece;\r
2181   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2182   HDC hdc;\r
2183   SIZE clockSize, messageSize;\r
2184   HFONT oldFont;\r
2185   char buf[MSG_SIZ];\r
2186   char *str;\r
2187   HMENU hmenu = GetMenu(hwndMain);\r
2188   RECT crect, wrect, oldRect;\r
2189   int offby;\r
2190   LOGBRUSH logbrush;\r
2191 \r
2192   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2193   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2194 \r
2195   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2196   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2197 \r
2198   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2199   oldRect.top = wpMain.y;\r
2200   oldRect.right = wpMain.x + wpMain.width;\r
2201   oldRect.bottom = wpMain.y + wpMain.height;\r
2202 \r
2203   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2204   smallLayout = sizeInfo[boardSize].smallLayout;\r
2205   squareSize = sizeInfo[boardSize].squareSize;\r
2206   lineGap = sizeInfo[boardSize].lineGap;\r
2207   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2208 \r
2209   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2210       lineGap = appData.overrideLineGap;\r
2211   }\r
2212 \r
2213   if (tinyLayout != oldTinyLayout) {\r
2214     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2215     if (tinyLayout) {\r
2216       style &= ~WS_SYSMENU;\r
2217       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2218                  "&Minimize\tCtrl+F4");\r
2219     } else {\r
2220       style |= WS_SYSMENU;\r
2221       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2222     }\r
2223     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2224 \r
2225     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2226       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2227         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2228     }\r
2229     DrawMenuBar(hwndMain);\r
2230   }\r
2231 \r
2232   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2233   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2234 \r
2235   /* Get text area sizes */\r
2236   hdc = GetDC(hwndMain);\r
2237   if (appData.clockMode) {\r
2238     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2239   } else {\r
2240     snprintf(buf, MSG_SIZ, _("White"));\r
2241   }\r
2242   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2243   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2244   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2245   str = _("We only care about the height here");\r
2246   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2247   SelectObject(hdc, oldFont);\r
2248   ReleaseDC(hwndMain, hdc);\r
2249 \r
2250   /* Compute where everything goes */\r
2251   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2252         /* [HGM] logo: if either logo is on, reserve space for it */\r
2253         logoHeight =  2*clockSize.cy;\r
2254         leftLogoRect.left   = OUTER_MARGIN;\r
2255         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2256         leftLogoRect.top    = OUTER_MARGIN;\r
2257         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2258 \r
2259         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2260         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2261         rightLogoRect.top    = OUTER_MARGIN;\r
2262         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2263 \r
2264 \r
2265     whiteRect.left = leftLogoRect.right;\r
2266     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2267     whiteRect.top = OUTER_MARGIN;\r
2268     whiteRect.bottom = whiteRect.top + logoHeight;\r
2269 \r
2270     blackRect.right = rightLogoRect.left;\r
2271     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2272     blackRect.top = whiteRect.top;\r
2273     blackRect.bottom = whiteRect.bottom;\r
2274   } else {\r
2275     whiteRect.left = OUTER_MARGIN;\r
2276     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2277     whiteRect.top = OUTER_MARGIN;\r
2278     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2279 \r
2280     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2281     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2282     blackRect.top = whiteRect.top;\r
2283     blackRect.bottom = whiteRect.bottom;\r
2284 \r
2285     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2286   }\r
2287 \r
2288   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2289   if (appData.showButtonBar) {\r
2290     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2291       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2292   } else {\r
2293     messageRect.right = OUTER_MARGIN + boardWidth;\r
2294   }\r
2295   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2296   messageRect.bottom = messageRect.top + messageSize.cy;\r
2297 \r
2298   boardRect.left = OUTER_MARGIN;\r
2299   boardRect.right = boardRect.left + boardWidth;\r
2300   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2301   boardRect.bottom = boardRect.top + boardHeight;\r
2302 \r
2303   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2304   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2305   oldBoardSize = boardSize;\r
2306   oldTinyLayout = tinyLayout;\r
2307   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2308   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2309     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2310   winW *= 1 + twoBoards;\r
2311   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2312   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2313   wpMain.height = winH; //       without disturbing window attachments\r
2314   GetWindowRect(hwndMain, &wrect);\r
2315   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2316                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2317 \r
2318   // [HGM] placement: let attached windows follow size change.\r
2319   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2320   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2321   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2322   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2323   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2324 \r
2325   /* compensate if menu bar wrapped */\r
2326   GetClientRect(hwndMain, &crect);\r
2327   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2328   wpMain.height += offby;\r
2329   switch (flags) {\r
2330   case WMSZ_TOPLEFT:\r
2331     SetWindowPos(hwndMain, NULL, \r
2332                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2333                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2334     break;\r
2335 \r
2336   case WMSZ_TOPRIGHT:\r
2337   case WMSZ_TOP:\r
2338     SetWindowPos(hwndMain, NULL, \r
2339                  wrect.left, wrect.bottom - wpMain.height, \r
2340                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2341     break;\r
2342 \r
2343   case WMSZ_BOTTOMLEFT:\r
2344   case WMSZ_LEFT:\r
2345     SetWindowPos(hwndMain, NULL, \r
2346                  wrect.right - wpMain.width, wrect.top, \r
2347                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2348     break;\r
2349 \r
2350   case WMSZ_BOTTOMRIGHT:\r
2351   case WMSZ_BOTTOM:\r
2352   case WMSZ_RIGHT:\r
2353   default:\r
2354     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2355                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2356     break;\r
2357   }\r
2358 \r
2359   hwndPause = NULL;\r
2360   for (i = 0; i < N_BUTTONS; i++) {\r
2361     if (buttonDesc[i].hwnd != NULL) {\r
2362       DestroyWindow(buttonDesc[i].hwnd);\r
2363       buttonDesc[i].hwnd = NULL;\r
2364     }\r
2365     if (appData.showButtonBar) {\r
2366       buttonDesc[i].hwnd =\r
2367         CreateWindow("BUTTON", buttonDesc[i].label,\r
2368                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2369                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2370                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2371                      (HMENU) buttonDesc[i].id,\r
2372                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2373       if (tinyLayout) {\r
2374         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2375                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2376                     MAKELPARAM(FALSE, 0));\r
2377       }\r
2378       if (buttonDesc[i].id == IDM_Pause)\r
2379         hwndPause = buttonDesc[i].hwnd;\r
2380       buttonDesc[i].wndproc = (WNDPROC)\r
2381         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2382     }\r
2383   }\r
2384   if (gridPen != NULL) DeleteObject(gridPen);\r
2385   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2386   if (premovePen != NULL) DeleteObject(premovePen);\r
2387   if (lineGap != 0) {\r
2388     logbrush.lbStyle = BS_SOLID;\r
2389     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2390     gridPen =\r
2391       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2392                    lineGap, &logbrush, 0, NULL);\r
2393     logbrush.lbColor = highlightSquareColor;\r
2394     highlightPen =\r
2395       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2396                    lineGap, &logbrush, 0, NULL);\r
2397 \r
2398     logbrush.lbColor = premoveHighlightColor; \r
2399     premovePen =\r
2400       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2401                    lineGap, &logbrush, 0, NULL);\r
2402 \r
2403     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2404     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2405       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2406       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2407         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2408       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2409         BOARD_WIDTH * (squareSize + lineGap);\r
2410       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2411     }\r
2412     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2413       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2414       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2415         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2416         lineGap / 2 + (i * (squareSize + lineGap));\r
2417       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2418         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2419       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2420     }\r
2421   }\r
2422 \r
2423   /* [HGM] Licensing requirement */\r
2424 #ifdef GOTHIC\r
2425   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2426 #endif\r
2427 #ifdef FALCON\r
2428   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2429 #endif\r
2430   GothicPopUp( "", VariantNormal);\r
2431 \r
2432 \r
2433 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2434 \r
2435   /* Load piece bitmaps for this board size */\r
2436   for (i=0; i<=2; i++) {\r
2437     for (piece = WhitePawn;\r
2438          (int) piece < (int) BlackPawn;\r
2439          piece = (ChessSquare) ((int) piece + 1)) {\r
2440       if (pieceBitmap[i][piece] != NULL)\r
2441         DeleteObject(pieceBitmap[i][piece]);\r
2442     }\r
2443   }\r
2444 \r
2445   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2446   // Orthodox Chess pieces\r
2447   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2448   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2449   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2450   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2451   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2452   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2453   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2454   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2455   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2456   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2457   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2458   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2459   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2460   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2461   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2462   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2463     // in Shogi, Hijack the unused Queen for Lance\r
2464     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2465     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2466     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2467   } else {\r
2468     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2469     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2470     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2471   }\r
2472 \r
2473   if(squareSize <= 72 && squareSize >= 33) { \r
2474     /* A & C are available in most sizes now */\r
2475     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2476       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2477       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2478       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2479       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2480       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2481       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2482       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2483       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2484       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2485       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2486       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2487       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2488     } else { // Smirf-like\r
2489       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2490       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2491       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2492     }\r
2493     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2494       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2495       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2496       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2497     } else { // WinBoard standard\r
2498       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2499       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2500       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2501     }\r
2502   }\r
2503 \r
2504 \r
2505   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2506     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2507     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2508     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2509     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2510     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2511     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2512     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2513     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2514     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2515     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2516     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2517     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2518     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2519     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2520     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2521     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2522     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2523     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2524     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2525     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2526     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2527     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2528     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2529     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2530     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2531     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2532     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2533     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2534     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2535     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2536 \r
2537     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2538       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2539       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2540       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2541       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2542       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2543       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2544       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2545       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2546       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2547       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2548       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2549       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2550     } else {\r
2551       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2552       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2553       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2554       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2555       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2556       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2557       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2558       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2559       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2560       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2561       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2562       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2563     }\r
2564 \r
2565   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2566     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2567     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2568     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2569     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2570     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2571     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2572     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2573     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2574     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2575     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2576     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2577     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2578     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2579     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2580   }\r
2581 \r
2582 \r
2583   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2584   /* special Shogi support in this size */\r
2585   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2586       for (piece = WhitePawn;\r
2587            (int) piece < (int) BlackPawn;\r
2588            piece = (ChessSquare) ((int) piece + 1)) {\r
2589         if (pieceBitmap[i][piece] != NULL)\r
2590           DeleteObject(pieceBitmap[i][piece]);\r
2591       }\r
2592     }\r
2593   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2594   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2595   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2596   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2597   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2598   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2599   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2600   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2601   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2602   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2603   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2604   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2605   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2606   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2607   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2608   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2609   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2610   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2611   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2612   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2613   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2614   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2615   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2616   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2617   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2618   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2619   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2620   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2621   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2622   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2623   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2624   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2625   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2626   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2627   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2628   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2629   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2630   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2631   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2632   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2633   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2634   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2635   minorSize = 0;\r
2636   }\r
2637 }\r
2638 \r
2639 HBITMAP\r
2640 PieceBitmap(ChessSquare p, int kind)\r
2641 {\r
2642   if ((int) p >= (int) BlackPawn)\r
2643     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2644 \r
2645   return pieceBitmap[kind][(int) p];\r
2646 }\r
2647 \r
2648 /***************************************************************/\r
2649 \r
2650 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2651 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2652 /*\r
2653 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2654 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2655 */\r
2656 \r
2657 VOID\r
2658 SquareToPos(int row, int column, int * x, int * y)\r
2659 {\r
2660   if (flipView) {\r
2661     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2662     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2663   } else {\r
2664     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2665     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2666   }\r
2667 }\r
2668 \r
2669 VOID\r
2670 DrawCoordsOnDC(HDC hdc)\r
2671 {\r
2672   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
2673   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
2674   char str[2] = { NULLCHAR, NULLCHAR };\r
2675   int oldMode, oldAlign, x, y, start, i;\r
2676   HFONT oldFont;\r
2677   HBRUSH oldBrush;\r
2678 \r
2679   if (!appData.showCoords)\r
2680     return;\r
2681 \r
2682   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2683 \r
2684   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2685   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2686   oldAlign = GetTextAlign(hdc);\r
2687   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2688 \r
2689   y = boardRect.top + lineGap;\r
2690   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2691 \r
2692   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2693   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2694     str[0] = files[start + i];\r
2695     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2696     y += squareSize + lineGap;\r
2697   }\r
2698 \r
2699   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2700 \r
2701   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2702   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2703     str[0] = ranks[start + i];\r
2704     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2705     x += squareSize + lineGap;\r
2706   }    \r
2707 \r
2708   SelectObject(hdc, oldBrush);\r
2709   SetBkMode(hdc, oldMode);\r
2710   SetTextAlign(hdc, oldAlign);\r
2711   SelectObject(hdc, oldFont);\r
2712 }\r
2713 \r
2714 VOID\r
2715 DrawGridOnDC(HDC hdc)\r
2716 {\r
2717   HPEN oldPen;\r
2718  \r
2719   if (lineGap != 0) {\r
2720     oldPen = SelectObject(hdc, gridPen);\r
2721     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2722     SelectObject(hdc, oldPen);\r
2723   }\r
2724 }\r
2725 \r
2726 #define HIGHLIGHT_PEN 0\r
2727 #define PREMOVE_PEN   1\r
2728 \r
2729 VOID\r
2730 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2731 {\r
2732   int x1, y1;\r
2733   HPEN oldPen, hPen;\r
2734   if (lineGap == 0) return;\r
2735   if (flipView) {\r
2736     x1 = boardRect.left +\r
2737       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2738     y1 = boardRect.top +\r
2739       lineGap/2 + y * (squareSize + lineGap);\r
2740   } else {\r
2741     x1 = boardRect.left +\r
2742       lineGap/2 + x * (squareSize + lineGap);\r
2743     y1 = boardRect.top +\r
2744       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2745   }\r
2746   hPen = pen ? premovePen : highlightPen;\r
2747   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2748   MoveToEx(hdc, x1, y1, NULL);\r
2749   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2750   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2751   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2752   LineTo(hdc, x1, y1);\r
2753   SelectObject(hdc, oldPen);\r
2754 }\r
2755 \r
2756 VOID\r
2757 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2758 {\r
2759   int i;\r
2760   for (i=0; i<2; i++) {\r
2761     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2762       DrawHighlightOnDC(hdc, TRUE,\r
2763                         h->sq[i].x, h->sq[i].y,\r
2764                         pen);\r
2765   }\r
2766 }\r
2767 \r
2768 /* Note: sqcolor is used only in monoMode */\r
2769 /* Note that this code is largely duplicated in woptions.c,\r
2770    function DrawSampleSquare, so that needs to be updated too */\r
2771 VOID\r
2772 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2773 {\r
2774   HBITMAP oldBitmap;\r
2775   HBRUSH oldBrush;\r
2776   int tmpSize;\r
2777 \r
2778   if (appData.blindfold) return;\r
2779 \r
2780   /* [AS] Use font-based pieces if needed */\r
2781   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2782     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2783     CreatePiecesFromFont();\r
2784 \r
2785     if( fontBitmapSquareSize == squareSize ) {\r
2786         int index = TranslatePieceToFontPiece(piece);\r
2787 \r
2788         SelectObject( tmphdc, hPieceMask[ index ] );\r
2789 \r
2790       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2791         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2792       else\r
2793         BitBlt( hdc,\r
2794             x, y,\r
2795             squareSize, squareSize,\r
2796             tmphdc,\r
2797             0, 0,\r
2798             SRCAND );\r
2799 \r
2800         SelectObject( tmphdc, hPieceFace[ index ] );\r
2801 \r
2802       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2803         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2804       else\r
2805         BitBlt( hdc,\r
2806             x, y,\r
2807             squareSize, squareSize,\r
2808             tmphdc,\r
2809             0, 0,\r
2810             SRCPAINT );\r
2811 \r
2812         return;\r
2813     }\r
2814   }\r
2815 \r
2816   if (appData.monoMode) {\r
2817     SelectObject(tmphdc, PieceBitmap(piece, \r
2818       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2819     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2820            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2821   } else {\r
2822     tmpSize = squareSize;\r
2823     if(minorSize &&\r
2824         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2825          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2826       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2827       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2828       x += (squareSize - minorSize)>>1;\r
2829       y += squareSize - minorSize - 2;\r
2830       tmpSize = minorSize;\r
2831     }\r
2832     if (color || appData.allWhite ) {\r
2833       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2834       if( color )\r
2835               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2836       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2837       if(appData.upsideDown && color==flipView)\r
2838         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2839       else\r
2840         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2841       /* Use black for outline of white pieces */\r
2842       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2843       if(appData.upsideDown && color==flipView)\r
2844         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2845       else\r
2846         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2847     } else {\r
2848       /* Use square color for details of black pieces */\r
2849       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2850       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2851       if(appData.upsideDown && !flipView)\r
2852         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2853       else\r
2854         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2855     }\r
2856     SelectObject(hdc, oldBrush);\r
2857     SelectObject(tmphdc, oldBitmap);\r
2858   }\r
2859 }\r
2860 \r
2861 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2862 int GetBackTextureMode( int algo )\r
2863 {\r
2864     int result = BACK_TEXTURE_MODE_DISABLED;\r
2865 \r
2866     switch( algo ) \r
2867     {\r
2868         case BACK_TEXTURE_MODE_PLAIN:\r
2869             result = 1; /* Always use identity map */\r
2870             break;\r
2871         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2872             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2873             break;\r
2874     }\r
2875 \r
2876     return result;\r
2877 }\r
2878 \r
2879 /* \r
2880     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2881     to handle redraws cleanly (as random numbers would always be different).\r
2882 */\r
2883 VOID RebuildTextureSquareInfo()\r
2884 {\r
2885     BITMAP bi;\r
2886     int lite_w = 0;\r
2887     int lite_h = 0;\r
2888     int dark_w = 0;\r
2889     int dark_h = 0;\r
2890     int row;\r
2891     int col;\r
2892 \r
2893     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2894 \r
2895     if( liteBackTexture != NULL ) {\r
2896         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2897             lite_w = bi.bmWidth;\r
2898             lite_h = bi.bmHeight;\r
2899         }\r
2900     }\r
2901 \r
2902     if( darkBackTexture != NULL ) {\r
2903         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2904             dark_w = bi.bmWidth;\r
2905             dark_h = bi.bmHeight;\r
2906         }\r
2907     }\r
2908 \r
2909     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2910         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2911             if( (col + row) & 1 ) {\r
2912                 /* Lite square */\r
2913                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2914                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2915                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2916                   else\r
2917                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2918                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2919                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2920                   else\r
2921                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2922                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2923                 }\r
2924             }\r
2925             else {\r
2926                 /* Dark square */\r
2927                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2928                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2929                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2930                   else\r
2931                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2932                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2933                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2934                   else\r
2935                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2936                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2937                 }\r
2938             }\r
2939         }\r
2940     }\r
2941 }\r
2942 \r
2943 /* [AS] Arrow highlighting support */\r
2944 \r
2945 static int A_WIDTH = 5; /* Width of arrow body */\r
2946 \r
2947 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2948 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2949 \r
2950 static double Sqr( double x )\r
2951 {\r
2952     return x*x;\r
2953 }\r
2954 \r
2955 static int Round( double x )\r
2956 {\r
2957     return (int) (x + 0.5);\r
2958 }\r
2959 \r
2960 /* Draw an arrow between two points using current settings */\r
2961 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2962 {\r
2963     POINT arrow[7];\r
2964     double dx, dy, j, k, x, y;\r
2965 \r
2966     if( d_x == s_x ) {\r
2967         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2968 \r
2969         arrow[0].x = s_x + A_WIDTH;\r
2970         arrow[0].y = s_y;\r
2971 \r
2972         arrow[1].x = s_x + A_WIDTH;\r
2973         arrow[1].y = d_y - h;\r
2974 \r
2975         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2976         arrow[2].y = d_y - h;\r
2977 \r
2978         arrow[3].x = d_x;\r
2979         arrow[3].y = d_y;\r
2980 \r
2981         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2982         arrow[4].y = d_y - h;\r
2983 \r
2984         arrow[5].x = s_x - A_WIDTH;\r
2985         arrow[5].y = d_y - h;\r
2986 \r
2987         arrow[6].x = s_x - A_WIDTH;\r
2988         arrow[6].y = s_y;\r
2989     }\r
2990     else if( d_y == s_y ) {\r
2991         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2992 \r
2993         arrow[0].x = s_x;\r
2994         arrow[0].y = s_y + A_WIDTH;\r
2995 \r
2996         arrow[1].x = d_x - w;\r
2997         arrow[1].y = s_y + A_WIDTH;\r
2998 \r
2999         arrow[2].x = d_x - w;\r
3000         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3001 \r
3002         arrow[3].x = d_x;\r
3003         arrow[3].y = d_y;\r
3004 \r
3005         arrow[4].x = d_x - w;\r
3006         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3007 \r
3008         arrow[5].x = d_x - w;\r
3009         arrow[5].y = s_y - A_WIDTH;\r
3010 \r
3011         arrow[6].x = s_x;\r
3012         arrow[6].y = s_y - A_WIDTH;\r
3013     }\r
3014     else {\r
3015         /* [AS] Needed a lot of paper for this! :-) */\r
3016         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3017         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3018   \r
3019         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3020 \r
3021         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3022 \r
3023         x = s_x;\r
3024         y = s_y;\r
3025 \r
3026         arrow[0].x = Round(x - j);\r
3027         arrow[0].y = Round(y + j*dx);\r
3028 \r
3029         arrow[1].x = Round(x + j);\r
3030         arrow[1].y = Round(y - j*dx);\r
3031 \r
3032         if( d_x > s_x ) {\r
3033             x = (double) d_x - k;\r
3034             y = (double) d_y - k*dy;\r
3035         }\r
3036         else {\r
3037             x = (double) d_x + k;\r
3038             y = (double) d_y + k*dy;\r
3039         }\r
3040 \r
3041         arrow[2].x = Round(x + j);\r
3042         arrow[2].y = Round(y - j*dx);\r
3043 \r
3044         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3045         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3046 \r
3047         arrow[4].x = d_x;\r
3048         arrow[4].y = d_y;\r
3049 \r
3050         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3051         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3052 \r
3053         arrow[6].x = Round(x - j);\r
3054         arrow[6].y = Round(y + j*dx);\r
3055     }\r
3056 \r
3057     Polygon( hdc, arrow, 7 );\r
3058 }\r
3059 \r
3060 /* [AS] Draw an arrow between two squares */\r
3061 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3062 {\r
3063     int s_x, s_y, d_x, d_y;\r
3064     HPEN hpen;\r
3065     HPEN holdpen;\r
3066     HBRUSH hbrush;\r
3067     HBRUSH holdbrush;\r
3068     LOGBRUSH stLB;\r
3069 \r
3070     if( s_col == d_col && s_row == d_row ) {\r
3071         return;\r
3072     }\r
3073 \r
3074     /* Get source and destination points */\r
3075     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3076     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3077 \r
3078     if( d_y > s_y ) {\r
3079         d_y += squareSize / 4;\r
3080     }\r
3081     else if( d_y < s_y ) {\r
3082         d_y += 3 * squareSize / 4;\r
3083     }\r
3084     else {\r
3085         d_y += squareSize / 2;\r
3086     }\r
3087 \r
3088     if( d_x > s_x ) {\r
3089         d_x += squareSize / 4;\r
3090     }\r
3091     else if( d_x < s_x ) {\r
3092         d_x += 3 * squareSize / 4;\r
3093     }\r
3094     else {\r
3095         d_x += squareSize / 2;\r
3096     }\r
3097 \r
3098     s_x += squareSize / 2;\r
3099     s_y += squareSize / 2;\r
3100 \r
3101     /* Adjust width */\r
3102     A_WIDTH = squareSize / 14;\r
3103 \r
3104     /* Draw */\r
3105     stLB.lbStyle = BS_SOLID;\r
3106     stLB.lbColor = appData.highlightArrowColor;\r
3107     stLB.lbHatch = 0;\r
3108 \r
3109     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3110     holdpen = SelectObject( hdc, hpen );\r
3111     hbrush = CreateBrushIndirect( &stLB );\r
3112     holdbrush = SelectObject( hdc, hbrush );\r
3113 \r
3114     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3115 \r
3116     SelectObject( hdc, holdpen );\r
3117     SelectObject( hdc, holdbrush );\r
3118     DeleteObject( hpen );\r
3119     DeleteObject( hbrush );\r
3120 }\r
3121 \r
3122 BOOL HasHighlightInfo()\r
3123 {\r
3124     BOOL result = FALSE;\r
3125 \r
3126     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3127         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3128     {\r
3129         result = TRUE;\r
3130     }\r
3131 \r
3132     return result;\r
3133 }\r
3134 \r
3135 BOOL IsDrawArrowEnabled()\r
3136 {\r
3137     BOOL result = FALSE;\r
3138 \r
3139     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3140         result = TRUE;\r
3141     }\r
3142 \r
3143     return result;\r
3144 }\r
3145 \r
3146 VOID DrawArrowHighlight( HDC hdc )\r
3147 {\r
3148     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3149         DrawArrowBetweenSquares( hdc,\r
3150             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3151             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3152     }\r
3153 }\r
3154 \r
3155 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3156 {\r
3157     HRGN result = NULL;\r
3158 \r
3159     if( HasHighlightInfo() ) {\r
3160         int x1, y1, x2, y2;\r
3161         int sx, sy, dx, dy;\r
3162 \r
3163         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3164         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3165 \r
3166         sx = MIN( x1, x2 );\r
3167         sy = MIN( y1, y2 );\r
3168         dx = MAX( x1, x2 ) + squareSize;\r
3169         dy = MAX( y1, y2 ) + squareSize;\r
3170 \r
3171         result = CreateRectRgn( sx, sy, dx, dy );\r
3172     }\r
3173 \r
3174     return result;\r
3175 }\r
3176 \r
3177 /*\r
3178     Warning: this function modifies the behavior of several other functions. \r
3179     \r
3180     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3181     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3182     repaint is scattered all over the place, which is not good for features such as\r
3183     "arrow highlighting" that require a full repaint of the board.\r
3184 \r
3185     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3186     user interaction, when speed is not so important) but especially to avoid errors\r
3187     in the displayed graphics.\r
3188 \r
3189     In such patched places, I always try refer to this function so there is a single\r
3190     place to maintain knowledge.\r
3191     \r
3192     To restore the original behavior, just return FALSE unconditionally.\r
3193 */\r
3194 BOOL IsFullRepaintPreferrable()\r
3195 {\r
3196     BOOL result = FALSE;\r
3197 \r
3198     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3199         /* Arrow may appear on the board */\r
3200         result = TRUE;\r
3201     }\r
3202 \r
3203     return result;\r
3204 }\r
3205 \r
3206 /* \r
3207     This function is called by DrawPosition to know whether a full repaint must\r
3208     be forced or not.\r
3209 \r
3210     Only DrawPosition may directly call this function, which makes use of \r
3211     some state information. Other function should call DrawPosition specifying \r
3212     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3213 */\r
3214 BOOL DrawPositionNeedsFullRepaint()\r
3215 {\r
3216     BOOL result = FALSE;\r
3217 \r
3218     /* \r
3219         Probably a slightly better policy would be to trigger a full repaint\r
3220         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3221         but animation is fast enough that it's difficult to notice.\r
3222     */\r
3223     if( animInfo.piece == EmptySquare ) {\r
3224         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3225             result = TRUE;\r
3226         }\r
3227     }\r
3228 \r
3229     return result;\r
3230 }\r
3231 \r
3232 VOID\r
3233 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3234 {\r
3235   int row, column, x, y, square_color, piece_color;\r
3236   ChessSquare piece;\r
3237   HBRUSH oldBrush;\r
3238   HDC texture_hdc = NULL;\r
3239 \r
3240   /* [AS] Initialize background textures if needed */\r
3241   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3242       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3243       if( backTextureSquareSize != squareSize \r
3244        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3245           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3246           backTextureSquareSize = squareSize;\r
3247           RebuildTextureSquareInfo();\r
3248       }\r
3249 \r
3250       texture_hdc = CreateCompatibleDC( hdc );\r
3251   }\r
3252 \r
3253   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3254     for (column = 0; column < BOARD_WIDTH; column++) {\r
3255   \r
3256       SquareToPos(row, column, &x, &y);\r
3257 \r
3258       piece = board[row][column];\r
3259 \r
3260       square_color = ((column + row) % 2) == 1;\r
3261       if( gameInfo.variant == VariantXiangqi ) {\r
3262           square_color = !InPalace(row, column);\r
3263           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3264           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3265       }\r
3266       piece_color = (int) piece < (int) BlackPawn;\r
3267 \r
3268 \r
3269       /* [HGM] holdings file: light square or black */\r
3270       if(column == BOARD_LEFT-2) {\r
3271             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3272                 square_color = 1;\r
3273             else {\r
3274                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3275                 continue;\r
3276             }\r
3277       } else\r
3278       if(column == BOARD_RGHT + 1 ) {\r
3279             if( row < gameInfo.holdingsSize )\r
3280                 square_color = 1;\r
3281             else {\r
3282                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3283                 continue;\r
3284             }\r
3285       }\r
3286       if(column == BOARD_LEFT-1 ) /* left align */\r
3287             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3288       else if( column == BOARD_RGHT) /* right align */\r
3289             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3290       else\r
3291       if (appData.monoMode) {\r
3292         if (piece == EmptySquare) {\r
3293           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3294                  square_color ? WHITENESS : BLACKNESS);\r
3295         } else {\r
3296           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3297         }\r
3298       } \r
3299       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3300           /* [AS] Draw the square using a texture bitmap */\r
3301           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3302           int r = row, c = column; // [HGM] do not flip board in flipView\r
3303           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3304 \r
3305           DrawTile( x, y, \r
3306               squareSize, squareSize, \r
3307               hdc, \r
3308               texture_hdc,\r
3309               backTextureSquareInfo[r][c].mode,\r
3310               backTextureSquareInfo[r][c].x,\r
3311               backTextureSquareInfo[r][c].y );\r
3312 \r
3313           SelectObject( texture_hdc, hbm );\r
3314 \r
3315           if (piece != EmptySquare) {\r
3316               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3317           }\r
3318       }\r
3319       else {\r
3320         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3321 \r
3322         oldBrush = SelectObject(hdc, brush );\r
3323         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3324         SelectObject(hdc, oldBrush);\r
3325         if (piece != EmptySquare)\r
3326           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3327       }\r
3328     }\r
3329   }\r
3330 \r
3331   if( texture_hdc != NULL ) {\r
3332     DeleteDC( texture_hdc );\r
3333   }\r
3334 }\r
3335 \r
3336 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3337 void fputDW(FILE *f, int x)\r
3338 {\r
3339         fputc(x     & 255, f);\r
3340         fputc(x>>8  & 255, f);\r
3341         fputc(x>>16 & 255, f);\r
3342         fputc(x>>24 & 255, f);\r
3343 }\r
3344 \r
3345 #define MAX_CLIPS 200   /* more than enough */\r
3346 \r
3347 VOID\r
3348 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3349 {\r
3350 //  HBITMAP bufferBitmap;\r
3351   BITMAP bi;\r
3352 //  RECT Rect;\r
3353   HDC tmphdc;\r
3354   HBITMAP hbm;\r
3355   int w = 100, h = 50;\r
3356 \r
3357   if(logo == NULL) return;\r
3358 //  GetClientRect(hwndMain, &Rect);\r
3359 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3360 //                                      Rect.bottom-Rect.top+1);\r
3361   tmphdc = CreateCompatibleDC(hdc);\r
3362   hbm = SelectObject(tmphdc, logo);\r
3363   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3364             w = bi.bmWidth;\r
3365             h = bi.bmHeight;\r
3366   }\r
3367   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3368                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3369   SelectObject(tmphdc, hbm);\r
3370   DeleteDC(tmphdc);\r
3371 }\r
3372 \r
3373 static HDC hdcSeek;\r
3374 \r
3375 // [HGM] seekgraph\r
3376 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3377 {\r
3378     POINT stPt;\r
3379     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3380     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3381     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3382     SelectObject( hdcSeek, hp );\r
3383 }\r
3384 \r
3385 // front-end wrapper for drawing functions to do rectangles\r
3386 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3387 {\r
3388     HPEN hp;\r
3389     RECT rc;\r
3390 \r
3391     if (hdcSeek == NULL) {\r
3392     hdcSeek = GetDC(hwndMain);\r
3393       if (!appData.monoMode) {\r
3394         SelectPalette(hdcSeek, hPal, FALSE);\r
3395         RealizePalette(hdcSeek);\r
3396       }\r
3397     }\r
3398     hp = SelectObject( hdcSeek, gridPen );\r
3399     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3400     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3401     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3402     SelectObject( hdcSeek, hp );\r
3403 }\r
3404 \r
3405 // front-end wrapper for putting text in graph\r
3406 void DrawSeekText(char *buf, int x, int y)\r
3407 {\r
3408         SIZE stSize;\r
3409         SetBkMode( hdcSeek, TRANSPARENT );\r
3410         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3411         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3412 }\r
3413 \r
3414 void DrawSeekDot(int x, int y, int color)\r
3415 {\r
3416         int square = color & 0x80;\r
3417         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3418                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3419         color &= 0x7F;\r
3420         if(square)\r
3421             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3422                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3423         else\r
3424             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3425                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3426             SelectObject(hdcSeek, oldBrush);\r
3427 }\r
3428 \r
3429 VOID\r
3430 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3431 {\r
3432   static Board lastReq[2], lastDrawn[2];\r
3433   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3434   static int lastDrawnFlipView = 0;\r
3435   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3436   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3437   HDC tmphdc;\r
3438   HDC hdcmem;\r
3439   HBITMAP bufferBitmap;\r
3440   HBITMAP oldBitmap;\r
3441   RECT Rect;\r
3442   HRGN clips[MAX_CLIPS];\r
3443   ChessSquare dragged_piece = EmptySquare;\r
3444   int nr = twoBoards*partnerUp;\r
3445 \r
3446   /* I'm undecided on this - this function figures out whether a full\r
3447    * repaint is necessary on its own, so there's no real reason to have the\r
3448    * caller tell it that.  I think this can safely be set to FALSE - but\r
3449    * if we trust the callers not to request full repaints unnessesarily, then\r
3450    * we could skip some clipping work.  In other words, only request a full\r
3451    * redraw when the majority of pieces have changed positions (ie. flip, \r
3452    * gamestart and similar)  --Hawk\r
3453    */\r
3454   Boolean fullrepaint = repaint;\r
3455 \r
3456   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3457 \r
3458   if( DrawPositionNeedsFullRepaint() ) {\r
3459       fullrepaint = TRUE;\r
3460   }\r
3461 \r
3462   if (board == NULL) {\r
3463     if (!lastReqValid[nr]) {\r
3464       return;\r
3465     }\r
3466     board = lastReq[nr];\r
3467   } else {\r
3468     CopyBoard(lastReq[nr], board);\r
3469     lastReqValid[nr] = 1;\r
3470   }\r
3471 \r
3472   if (doingSizing) {\r
3473     return;\r
3474   }\r
3475 \r
3476   if (IsIconic(hwndMain)) {\r
3477     return;\r
3478   }\r
3479 \r
3480   if (hdc == NULL) {\r
3481     hdc = GetDC(hwndMain);\r
3482     if (!appData.monoMode) {\r
3483       SelectPalette(hdc, hPal, FALSE);\r
3484       RealizePalette(hdc);\r
3485     }\r
3486     releaseDC = TRUE;\r
3487   } else {\r
3488     releaseDC = FALSE;\r
3489   }\r
3490 \r
3491   /* Create some work-DCs */\r
3492   hdcmem = CreateCompatibleDC(hdc);\r
3493   tmphdc = CreateCompatibleDC(hdc);\r
3494 \r
3495   /* If dragging is in progress, we temporarely remove the piece */\r
3496   /* [HGM] or temporarily decrease count if stacked              */\r
3497   /*       !! Moved to before board compare !!                   */\r
3498   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3499     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3500     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3501             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3502         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3503     } else \r
3504     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3505             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3506         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3507     } else \r
3508         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3509   }\r
3510 \r
3511   /* Figure out which squares need updating by comparing the \r
3512    * newest board with the last drawn board and checking if\r
3513    * flipping has changed.\r
3514    */\r
3515   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3516     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3517       for (column = 0; column < BOARD_WIDTH; column++) {\r
3518         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3519           SquareToPos(row, column, &x, &y);\r
3520           clips[num_clips++] =\r
3521             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3522         }\r
3523       }\r
3524     }\r
3525    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3526     for (i=0; i<2; i++) {\r
3527       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3528           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3529         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3530             lastDrawnHighlight.sq[i].y >= 0) {\r
3531           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3532                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3533           clips[num_clips++] =\r
3534             CreateRectRgn(x - lineGap, y - lineGap, \r
3535                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3536         }\r
3537         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3538           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3539           clips[num_clips++] =\r
3540             CreateRectRgn(x - lineGap, y - lineGap, \r
3541                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3542         }\r
3543       }\r
3544     }\r
3545     for (i=0; i<2; i++) {\r
3546       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3547           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3548         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3549             lastDrawnPremove.sq[i].y >= 0) {\r
3550           SquareToPos(lastDrawnPremove.sq[i].y,\r
3551                       lastDrawnPremove.sq[i].x, &x, &y);\r
3552           clips[num_clips++] =\r
3553             CreateRectRgn(x - lineGap, y - lineGap, \r
3554                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3555         }\r
3556         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3557             premoveHighlightInfo.sq[i].y >= 0) {\r
3558           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3559                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3560           clips[num_clips++] =\r
3561             CreateRectRgn(x - lineGap, y - lineGap, \r
3562                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3563         }\r
3564       }\r
3565     }\r
3566    } else { // nr == 1\r
3567         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3568         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3569         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3570         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3571       for (i=0; i<2; i++) {\r
3572         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3573             partnerHighlightInfo.sq[i].y >= 0) {\r
3574           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3575                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3576           clips[num_clips++] =\r
3577             CreateRectRgn(x - lineGap, y - lineGap, \r
3578                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3579         }\r
3580         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3581             oldPartnerHighlight.sq[i].y >= 0) {\r
3582           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3583                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3584           clips[num_clips++] =\r
3585             CreateRectRgn(x - lineGap, y - lineGap, \r
3586                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3587         }\r
3588       }\r
3589    }\r
3590   } else {\r
3591     fullrepaint = TRUE;\r
3592   }\r
3593 \r
3594   /* Create a buffer bitmap - this is the actual bitmap\r
3595    * being written to.  When all the work is done, we can\r
3596    * copy it to the real DC (the screen).  This avoids\r
3597    * the problems with flickering.\r
3598    */\r
3599   GetClientRect(hwndMain, &Rect);\r
3600   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3601                                         Rect.bottom-Rect.top+1);\r
3602   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3603   if (!appData.monoMode) {\r
3604     SelectPalette(hdcmem, hPal, FALSE);\r
3605   }\r
3606 \r
3607   /* Create clips for dragging */\r
3608   if (!fullrepaint) {\r
3609     if (dragInfo.from.x >= 0) {\r
3610       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3611       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3612     }\r
3613     if (dragInfo.start.x >= 0) {\r
3614       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3615       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3616     }\r
3617     if (dragInfo.pos.x >= 0) {\r
3618       x = dragInfo.pos.x - squareSize / 2;\r
3619       y = dragInfo.pos.y - squareSize / 2;\r
3620       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3621     }\r
3622     if (dragInfo.lastpos.x >= 0) {\r
3623       x = dragInfo.lastpos.x - squareSize / 2;\r
3624       y = dragInfo.lastpos.y - squareSize / 2;\r
3625       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3626     }\r
3627   }\r
3628 \r
3629   /* Are we animating a move?  \r
3630    * If so, \r
3631    *   - remove the piece from the board (temporarely)\r
3632    *   - calculate the clipping region\r
3633    */\r
3634   if (!fullrepaint) {\r
3635     if (animInfo.piece != EmptySquare) {\r
3636       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3637       x = boardRect.left + animInfo.lastpos.x;\r
3638       y = boardRect.top + animInfo.lastpos.y;\r
3639       x2 = boardRect.left + animInfo.pos.x;\r
3640       y2 = boardRect.top + animInfo.pos.y;\r
3641       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3642       /* Slight kludge.  The real problem is that after AnimateMove is\r
3643          done, the position on the screen does not match lastDrawn.\r
3644          This currently causes trouble only on e.p. captures in\r
3645          atomic, where the piece moves to an empty square and then\r
3646          explodes.  The old and new positions both had an empty square\r
3647          at the destination, but animation has drawn a piece there and\r
3648          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3649       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3650     }\r
3651   }\r
3652 \r
3653   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3654   if (num_clips == 0)\r
3655     fullrepaint = TRUE;\r
3656 \r
3657   /* Set clipping on the memory DC */\r
3658   if (!fullrepaint) {\r
3659     SelectClipRgn(hdcmem, clips[0]);\r
3660     for (x = 1; x < num_clips; x++) {\r
3661       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3662         abort();  // this should never ever happen!\r
3663     }\r
3664   }\r
3665 \r
3666   /* Do all the drawing to the memory DC */\r
3667   if(explodeInfo.radius) { // [HGM] atomic\r
3668         HBRUSH oldBrush;\r
3669         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3670         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3671         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3672         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3673         x += squareSize/2;\r
3674         y += squareSize/2;\r
3675         if(!fullrepaint) {\r
3676           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3677           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3678         }\r
3679         DrawGridOnDC(hdcmem);\r
3680         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3681         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3682         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3683         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3684         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3685         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3686         SelectObject(hdcmem, oldBrush);\r
3687   } else {\r
3688     DrawGridOnDC(hdcmem);\r
3689     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3690         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3691         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3692     } else {\r
3693         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3694         oldPartnerHighlight = partnerHighlightInfo;\r
3695     }\r
3696     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3697   }\r
3698   if(nr == 0) // [HGM] dual: markers only on left board\r
3699   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3700     for (column = 0; column < BOARD_WIDTH; column++) {\r
3701         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3702             HBRUSH oldBrush = SelectObject(hdcmem, \r
3703                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3704             SquareToPos(row, column, &x, &y);\r
3705             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3706                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3707             SelectObject(hdcmem, oldBrush);\r
3708         }\r
3709     }\r
3710   }\r
3711   if(logoHeight) {\r
3712         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3713         if(appData.autoLogo) {\r
3714           \r
3715           switch(gameMode) { // pick logos based on game mode\r
3716             case IcsObserving:\r
3717                 whiteLogo = second.programLogo; // ICS logo\r
3718                 blackLogo = second.programLogo;\r
3719             default:\r
3720                 break;\r
3721             case IcsPlayingWhite:\r
3722                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3723                 blackLogo = second.programLogo; // ICS logo\r
3724                 break;\r
3725             case IcsPlayingBlack:\r
3726                 whiteLogo = second.programLogo; // ICS logo\r
3727                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3728                 break;\r
3729             case TwoMachinesPlay:\r
3730                 if(first.twoMachinesColor[0] == 'b') {\r
3731                     whiteLogo = second.programLogo;\r
3732                     blackLogo = first.programLogo;\r
3733                 }\r
3734                 break;\r
3735             case MachinePlaysWhite:\r
3736                 blackLogo = userLogo;\r
3737                 break;\r
3738             case MachinePlaysBlack:\r
3739                 whiteLogo = userLogo;\r
3740                 blackLogo = first.programLogo;\r
3741           }\r
3742         }\r
3743         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3744         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3745   }\r
3746 \r
3747   if( appData.highlightMoveWithArrow ) {\r
3748     DrawArrowHighlight(hdcmem);\r
3749   }\r
3750 \r
3751   DrawCoordsOnDC(hdcmem);\r
3752 \r
3753   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3754                  /* to make sure lastDrawn contains what is actually drawn */\r
3755 \r
3756   /* Put the dragged piece back into place and draw it (out of place!) */\r
3757     if (dragged_piece != EmptySquare) {\r
3758     /* [HGM] or restack */\r
3759     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3760                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3761     else\r
3762     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3763                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3764     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3765     x = dragInfo.pos.x - squareSize / 2;\r
3766     y = dragInfo.pos.y - squareSize / 2;\r
3767     DrawPieceOnDC(hdcmem, dragged_piece,\r
3768                   ((int) dragged_piece < (int) BlackPawn), \r
3769                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3770   }   \r
3771   \r
3772   /* Put the animated piece back into place and draw it */\r
3773   if (animInfo.piece != EmptySquare) {\r
3774     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3775     x = boardRect.left + animInfo.pos.x;\r
3776     y = boardRect.top + animInfo.pos.y;\r
3777     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3778                   ((int) animInfo.piece < (int) BlackPawn),\r
3779                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3780   }\r
3781 \r
3782   /* Release the bufferBitmap by selecting in the old bitmap \r
3783    * and delete the memory DC\r
3784    */\r
3785   SelectObject(hdcmem, oldBitmap);\r
3786   DeleteDC(hdcmem);\r
3787 \r
3788   /* Set clipping on the target DC */\r
3789   if (!fullrepaint) {\r
3790     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3791         RECT rect;\r
3792         GetRgnBox(clips[x], &rect);\r
3793         DeleteObject(clips[x]);\r
3794         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3795                           rect.right + wpMain.width/2, rect.bottom);\r
3796     }\r
3797     SelectClipRgn(hdc, clips[0]);\r
3798     for (x = 1; x < num_clips; x++) {\r
3799       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3800         abort();   // this should never ever happen!\r
3801     } \r
3802   }\r
3803 \r
3804   /* Copy the new bitmap onto the screen in one go.\r
3805    * This way we avoid any flickering\r
3806    */\r
3807   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3808   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3809          boardRect.right - boardRect.left,\r
3810          boardRect.bottom - boardRect.top,\r
3811          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3812   if(saveDiagFlag) { \r
3813     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3814     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3815 \r
3816     GetObject(bufferBitmap, sizeof(b), &b);\r
3817     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3818         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3819         bih.biWidth = b.bmWidth;\r
3820         bih.biHeight = b.bmHeight;\r
3821         bih.biPlanes = 1;\r
3822         bih.biBitCount = b.bmBitsPixel;\r
3823         bih.biCompression = 0;\r
3824         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3825         bih.biXPelsPerMeter = 0;\r
3826         bih.biYPelsPerMeter = 0;\r
3827         bih.biClrUsed = 0;\r
3828         bih.biClrImportant = 0;\r
3829 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3830 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3831         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3832 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3833 \r
3834         wb = b.bmWidthBytes;\r
3835         // count colors\r
3836         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3837                 int k = ((int*) pData)[i];\r
3838                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3839                 if(j >= 16) break;\r
3840                 color[j] = k;\r
3841                 if(j >= nrColors) nrColors = j+1;\r
3842         }\r
3843         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3844                 INT p = 0;\r
3845                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3846                     for(w=0; w<(wb>>2); w+=2) {\r
3847                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3848                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3849                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3850                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3851                         pData[p++] = m | j<<4;\r
3852                     }\r
3853                     while(p&3) pData[p++] = 0;\r
3854                 }\r
3855                 fac = 3;\r
3856                 wb = ((wb+31)>>5)<<2;\r
3857         }\r
3858         // write BITMAPFILEHEADER\r
3859         fprintf(diagFile, "BM");\r
3860         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3861         fputDW(diagFile, 0);\r
3862         fputDW(diagFile, 0x36 + (fac?64:0));\r
3863         // write BITMAPINFOHEADER\r
3864         fputDW(diagFile, 40);\r
3865         fputDW(diagFile, b.bmWidth);\r
3866         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3867         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3868         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3869         fputDW(diagFile, 0);\r
3870         fputDW(diagFile, 0);\r
3871         fputDW(diagFile, 0);\r
3872         fputDW(diagFile, 0);\r
3873         fputDW(diagFile, 0);\r
3874         fputDW(diagFile, 0);\r
3875         // write color table\r
3876         if(fac)\r
3877         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3878         // write bitmap data\r
3879         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3880                 fputc(pData[i], diagFile);\r
3881      }\r
3882   }\r
3883 \r
3884   SelectObject(tmphdc, oldBitmap);\r
3885 \r
3886   /* Massive cleanup */\r
3887   for (x = 0; x < num_clips; x++)\r
3888     DeleteObject(clips[x]);\r
3889 \r
3890   DeleteDC(tmphdc);\r
3891   DeleteObject(bufferBitmap);\r
3892 \r
3893   if (releaseDC) \r
3894     ReleaseDC(hwndMain, hdc);\r
3895   \r
3896   if (lastDrawnFlipView != flipView && nr == 0) {\r
3897     if (flipView)\r
3898       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3899     else\r
3900       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3901   }\r
3902 \r
3903 /*  CopyBoard(lastDrawn, board);*/\r
3904   lastDrawnHighlight = highlightInfo;\r
3905   lastDrawnPremove   = premoveHighlightInfo;\r
3906   lastDrawnFlipView = flipView;\r
3907   lastDrawnValid[nr] = 1;\r
3908 }\r
3909 \r
3910 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3911 int\r
3912 SaveDiagram(f)\r
3913      FILE *f;\r
3914 {\r
3915     saveDiagFlag = 1; diagFile = f;\r
3916     HDCDrawPosition(NULL, TRUE, NULL);\r
3917 \r
3918     saveDiagFlag = 0;\r
3919 \r
3920 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3921     \r
3922     fclose(f);\r
3923     return TRUE;\r
3924 }\r
3925 \r
3926 \r
3927 /*---------------------------------------------------------------------------*\\r
3928 | CLIENT PAINT PROCEDURE\r
3929 |   This is the main event-handler for the WM_PAINT message.\r
3930 |\r
3931 \*---------------------------------------------------------------------------*/\r
3932 VOID\r
3933 PaintProc(HWND hwnd)\r
3934 {\r
3935   HDC         hdc;\r
3936   PAINTSTRUCT ps;\r
3937   HFONT       oldFont;\r
3938 \r
3939   if((hdc = BeginPaint(hwnd, &ps))) {\r
3940     if (IsIconic(hwnd)) {\r
3941       DrawIcon(hdc, 2, 2, iconCurrent);\r
3942     } else {\r
3943       if (!appData.monoMode) {\r
3944         SelectPalette(hdc, hPal, FALSE);\r
3945         RealizePalette(hdc);\r
3946       }\r
3947       HDCDrawPosition(hdc, 1, NULL);\r
3948       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3949         flipView = !flipView; partnerUp = !partnerUp;\r
3950         HDCDrawPosition(hdc, 1, NULL);\r
3951         flipView = !flipView; partnerUp = !partnerUp;\r
3952       }\r
3953       oldFont =\r
3954         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3955       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3956                  ETO_CLIPPED|ETO_OPAQUE,\r
3957                  &messageRect, messageText, strlen(messageText), NULL);\r
3958       SelectObject(hdc, oldFont);\r
3959       DisplayBothClocks();\r
3960     }\r
3961     EndPaint(hwnd,&ps);\r
3962   }\r
3963 \r
3964   return;\r
3965 }\r
3966 \r
3967 \r
3968 /*\r
3969  * If the user selects on a border boundary, return -1; if off the board,\r
3970  *   return -2.  Otherwise map the event coordinate to the square.\r
3971  * The offset boardRect.left or boardRect.top must already have been\r
3972  *   subtracted from x.\r
3973  */\r
3974 int EventToSquare(x, limit)\r
3975      int x, limit;\r
3976 {\r
3977   if (x <= 0)\r
3978     return -2;\r
3979   if (x < lineGap)\r
3980     return -1;\r
3981   x -= lineGap;\r
3982   if ((x % (squareSize + lineGap)) >= squareSize)\r
3983     return -1;\r
3984   x /= (squareSize + lineGap);\r
3985     if (x >= limit)\r
3986     return -2;\r
3987   return x;\r
3988 }\r
3989 \r
3990 typedef struct {\r
3991   char piece;\r
3992   int command;\r
3993   char* name;\r
3994 } DropEnable;\r
3995 \r
3996 DropEnable dropEnables[] = {\r
3997   { 'P', DP_Pawn, N_("Pawn") },\r
3998   { 'N', DP_Knight, N_("Knight") },\r
3999   { 'B', DP_Bishop, N_("Bishop") },\r
4000   { 'R', DP_Rook, N_("Rook") },\r
4001   { 'Q', DP_Queen, N_("Queen") },\r
4002 };\r
4003 \r
4004 VOID\r
4005 SetupDropMenu(HMENU hmenu)\r
4006 {\r
4007   int i, count, enable;\r
4008   char *p;\r
4009   extern char white_holding[], black_holding[];\r
4010   char item[MSG_SIZ];\r
4011 \r
4012   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4013     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4014                dropEnables[i].piece);\r
4015     count = 0;\r
4016     while (p && *p++ == dropEnables[i].piece) count++;\r
4017       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4018     enable = count > 0 || !appData.testLegality\r
4019       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4020                       && !appData.icsActive);\r
4021     ModifyMenu(hmenu, dropEnables[i].command,\r
4022                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4023                dropEnables[i].command, item);\r
4024   }\r
4025 }\r
4026 \r
4027 void DragPieceBegin(int x, int y)\r
4028 {\r
4029       dragInfo.lastpos.x = boardRect.left + x;\r
4030       dragInfo.lastpos.y = boardRect.top + y;\r
4031       dragInfo.from.x = fromX;\r
4032       dragInfo.from.y = fromY;\r
4033       dragInfo.start = dragInfo.from;\r
4034       SetCapture(hwndMain);\r
4035 }\r
4036 \r
4037 void DragPieceEnd(int x, int y)\r
4038 {\r
4039     ReleaseCapture();\r
4040     dragInfo.start.x = dragInfo.start.y = -1;\r
4041     dragInfo.from = dragInfo.start;\r
4042     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4043 }\r
4044 \r
4045 /* Event handler for mouse messages */\r
4046 VOID\r
4047 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4048 {\r
4049   int x, y, menuNr;\r
4050   POINT pt;\r
4051   static int recursive = 0;\r
4052   HMENU hmenu;\r
4053   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4054 \r
4055   if (recursive) {\r
4056     if (message == WM_MBUTTONUP) {\r
4057       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4058          to the middle button: we simulate pressing the left button too!\r
4059          */\r
4060       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4061       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4062     }\r
4063     return;\r
4064   }\r
4065   recursive++;\r
4066   \r
4067   pt.x = LOWORD(lParam);\r
4068   pt.y = HIWORD(lParam);\r
4069   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4070   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4071   if (!flipView && y >= 0) {\r
4072     y = BOARD_HEIGHT - 1 - y;\r
4073   }\r
4074   if (flipView && x >= 0) {\r
4075     x = BOARD_WIDTH - 1 - x;\r
4076   }\r
4077 \r
4078   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4079 \r
4080   switch (message) {\r
4081   case WM_LBUTTONDOWN:\r
4082       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4083         if (gameMode == EditPosition) {\r
4084           SetWhiteToPlayEvent();\r
4085         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4086           AdjustClock(flipClock, -1);\r
4087         } else if (gameMode == IcsPlayingBlack ||\r
4088                    gameMode == MachinePlaysWhite) {\r
4089           CallFlagEvent();\r
4090         }\r
4091       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4092         if (gameMode == EditPosition) {\r
4093           SetBlackToPlayEvent();\r
4094         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4095           AdjustClock(!flipClock, -1);\r
4096         } else if (gameMode == IcsPlayingWhite ||\r
4097                    gameMode == MachinePlaysBlack) {\r
4098           CallFlagEvent();\r
4099         }\r
4100       }\r
4101       dragInfo.start.x = dragInfo.start.y = -1;\r
4102       dragInfo.from = dragInfo.start;\r
4103     if(fromX == -1 && frozen) { // not sure where this is for\r
4104                 fromX = fromY = -1; \r
4105       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4106       break;\r
4107     }\r
4108       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4109       DrawPosition(TRUE, NULL);\r
4110     break;\r
4111 \r
4112   case WM_LBUTTONUP:\r
4113       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4114       DrawPosition(TRUE, NULL);\r
4115     break;\r
4116 \r
4117   case WM_MOUSEMOVE:\r
4118     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4119     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4120     if ((appData.animateDragging || appData.highlightDragging)\r
4121         && (wParam & MK_LBUTTON)\r
4122         && dragInfo.from.x >= 0) \r
4123     {\r
4124       BOOL full_repaint = FALSE;\r
4125 \r
4126       if (appData.animateDragging) {\r
4127         dragInfo.pos = pt;\r
4128       }\r
4129       if (appData.highlightDragging) {\r
4130         SetHighlights(fromX, fromY, x, y);\r
4131         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4132             full_repaint = TRUE;\r
4133         }\r
4134       }\r
4135       \r
4136       DrawPosition( full_repaint, NULL);\r
4137       \r
4138       dragInfo.lastpos = dragInfo.pos;\r
4139     }\r
4140     break;\r
4141 \r
4142   case WM_MOUSEWHEEL: // [DM]\r
4143     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4144        /* Mouse Wheel is being rolled forward\r
4145         * Play moves forward\r
4146         */\r
4147        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4148                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4149        /* Mouse Wheel is being rolled backward\r
4150         * Play moves backward\r
4151         */\r
4152        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4153                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4154     }\r
4155     break;\r
4156 \r
4157   case WM_MBUTTONUP:\r
4158   case WM_RBUTTONUP:\r
4159     ReleaseCapture();\r
4160     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4161     break;\r
4162  \r
4163   case WM_MBUTTONDOWN:\r
4164   case WM_RBUTTONDOWN:\r
4165     ErrorPopDown();\r
4166     ReleaseCapture();\r
4167     fromX = fromY = -1;\r
4168     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4169     dragInfo.start.x = dragInfo.start.y = -1;\r
4170     dragInfo.from = dragInfo.start;\r
4171     dragInfo.lastpos = dragInfo.pos;\r
4172     if (appData.highlightDragging) {\r
4173       ClearHighlights();\r
4174     }\r
4175     if(y == -2) {\r
4176       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4177       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4178           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4179       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4180           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4181       }\r
4182       break;\r
4183     }\r
4184     DrawPosition(TRUE, NULL);\r
4185 \r
4186     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4187     switch (menuNr) {\r
4188     case 0:\r
4189       if (message == WM_MBUTTONDOWN) {\r
4190         buttonCount = 3;  /* even if system didn't think so */\r
4191         if (wParam & MK_SHIFT) \r
4192           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4193         else\r
4194           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4195       } else { /* message == WM_RBUTTONDOWN */\r
4196         /* Just have one menu, on the right button.  Windows users don't\r
4197            think to try the middle one, and sometimes other software steals\r
4198            it, or it doesn't really exist. */\r
4199         if(gameInfo.variant != VariantShogi)\r
4200             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4201         else\r
4202             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4203       }\r
4204       break;\r
4205     case 2:\r
4206       SetCapture(hwndMain);
4207       break;\r
4208     case 1:\r
4209       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4210       SetupDropMenu(hmenu);\r
4211       MenuPopup(hwnd, pt, hmenu, -1);\r
4212     default:\r
4213       break;\r
4214     }\r
4215     break;\r
4216   }\r
4217 \r
4218   recursive--;\r
4219 }\r
4220 \r
4221 /* Preprocess messages for buttons in main window */\r
4222 LRESULT CALLBACK\r
4223 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4224 {\r
4225   int id = GetWindowLong(hwnd, GWL_ID);\r
4226   int i, dir;\r
4227 \r
4228   for (i=0; i<N_BUTTONS; i++) {\r
4229     if (buttonDesc[i].id == id) break;\r
4230   }\r
4231   if (i == N_BUTTONS) return 0;\r
4232   switch (message) {\r
4233   case WM_KEYDOWN:\r
4234     switch (wParam) {\r
4235     case VK_LEFT:\r
4236     case VK_RIGHT:\r
4237       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4238       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4239       return TRUE;\r
4240     }\r
4241     break;\r
4242   case WM_CHAR:\r
4243     switch (wParam) {\r
4244     case '\r':\r
4245       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4246       return TRUE;\r
4247     default:\r
4248       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4249         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4250         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4251         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4252         SetFocus(h);\r
4253         SendMessage(h, WM_CHAR, wParam, lParam);\r
4254         return TRUE;\r
4255       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4256         PopUpMoveDialog((char)wParam);\r
4257       }\r
4258       break;\r
4259     }\r
4260     break;\r
4261   }\r
4262   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4263 }\r
4264 \r
4265 /* Process messages for Promotion dialog box */\r
4266 LRESULT CALLBACK\r
4267 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4268 {\r
4269   char promoChar;\r
4270 \r
4271   switch (message) {\r
4272   case WM_INITDIALOG: /* message: initialize dialog box */\r
4273     /* Center the dialog over the application window */\r
4274     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4275     Translate(hDlg, DLG_PromotionKing);\r
4276     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4277       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4278        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4279                SW_SHOW : SW_HIDE);\r
4280     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4281     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4282        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4283          PieceToChar(WhiteAngel) != '~') ||\r
4284         (PieceToChar(BlackAngel) >= 'A' &&\r
4285          PieceToChar(BlackAngel) != '~')   ) ?\r
4286                SW_SHOW : SW_HIDE);\r
4287     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4288        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4289          PieceToChar(WhiteMarshall) != '~') ||\r
4290         (PieceToChar(BlackMarshall) >= 'A' &&\r
4291          PieceToChar(BlackMarshall) != '~')   ) ?\r
4292                SW_SHOW : SW_HIDE);\r
4293     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4294     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4295        gameInfo.variant != VariantShogi ?\r
4296                SW_SHOW : SW_HIDE);\r
4297     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4298        gameInfo.variant != VariantShogi ?\r
4299                SW_SHOW : SW_HIDE);\r
4300     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4301        gameInfo.variant == VariantShogi ?\r
4302                SW_SHOW : SW_HIDE);\r
4303     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4304        gameInfo.variant == VariantShogi ?\r
4305                SW_SHOW : SW_HIDE);\r
4306     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4307        gameInfo.variant == VariantSuper ?\r
4308                SW_SHOW : SW_HIDE);\r
4309     return TRUE;\r
4310 \r
4311   case WM_COMMAND: /* message: received a command */\r
4312     switch (LOWORD(wParam)) {\r
4313     case IDCANCEL:\r
4314       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4315       ClearHighlights();\r
4316       DrawPosition(FALSE, NULL);\r
4317       return TRUE;\r
4318     case PB_King:\r
4319       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4320       break;\r
4321     case PB_Queen:\r
4322       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4323       break;\r
4324     case PB_Rook:\r
4325       promoChar = PieceToChar(BlackRook);\r
4326       break;\r
4327     case PB_Bishop:\r
4328       promoChar = PieceToChar(BlackBishop);\r
4329       break;\r
4330     case PB_Chancellor:\r
4331       promoChar = PieceToChar(BlackMarshall);\r
4332       break;\r
4333     case PB_Archbishop:\r
4334       promoChar = PieceToChar(BlackAngel);\r
4335       break;\r
4336     case PB_Knight:\r
4337       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4338       break;\r
4339     default:\r
4340       return FALSE;\r
4341     }\r
4342     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4343     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4344        only show the popup when we are already sure the move is valid or\r
4345        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4346        will figure out it is a promotion from the promoChar. */\r
4347     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4348     fromX = fromY = -1;\r
4349     if (!appData.highlightLastMove) {\r
4350       ClearHighlights();\r
4351       DrawPosition(FALSE, NULL);\r
4352     }\r
4353     return TRUE;\r
4354   }\r
4355   return FALSE;\r
4356 }\r
4357 \r
4358 /* Pop up promotion dialog */\r
4359 VOID\r
4360 PromotionPopup(HWND hwnd)\r
4361 {\r
4362   FARPROC lpProc;\r
4363 \r
4364   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4365   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4366     hwnd, (DLGPROC)lpProc);\r
4367   FreeProcInstance(lpProc);\r
4368 }\r
4369 \r
4370 void\r
4371 PromotionPopUp()\r
4372 {\r
4373   DrawPosition(TRUE, NULL);\r
4374   PromotionPopup(hwndMain);\r
4375 }\r
4376 \r
4377 /* Toggle ShowThinking */\r
4378 VOID\r
4379 ToggleShowThinking()\r
4380 {\r
4381   appData.showThinking = !appData.showThinking;\r
4382   ShowThinkingEvent();\r
4383 }\r
4384 \r
4385 VOID\r
4386 LoadGameDialog(HWND hwnd, char* title)\r
4387 {\r
4388   UINT number = 0;\r
4389   FILE *f;\r
4390   char fileTitle[MSG_SIZ];\r
4391   f = OpenFileDialog(hwnd, "rb", "",\r
4392                      appData.oldSaveStyle ? "gam" : "pgn",\r
4393                      GAME_FILT,\r
4394                      title, &number, fileTitle, NULL);\r
4395   if (f != NULL) {\r
4396     cmailMsgLoaded = FALSE;\r
4397     if (number == 0) {\r
4398       int error = GameListBuild(f);\r
4399       if (error) {\r
4400         DisplayError(_("Cannot build game list"), error);\r
4401       } else if (!ListEmpty(&gameList) &&\r
4402                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4403         GameListPopUp(f, fileTitle);\r
4404         return;\r
4405       }\r
4406       GameListDestroy();\r
4407       number = 1;\r
4408     }\r
4409     LoadGame(f, number, fileTitle, FALSE);\r
4410   }\r
4411 }\r
4412 \r
4413 int get_term_width()\r
4414 {\r
4415     HDC hdc;\r
4416     TEXTMETRIC tm;\r
4417     RECT rc;\r
4418     HFONT hfont, hold_font;\r
4419     LOGFONT lf;\r
4420     HWND hText;\r
4421 \r
4422     if (hwndConsole)\r
4423         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4424     else\r
4425         return 79;\r
4426 \r
4427     // get the text metrics\r
4428     hdc = GetDC(hText);\r
4429     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4430     if (consoleCF.dwEffects & CFE_BOLD)\r
4431         lf.lfWeight = FW_BOLD;\r
4432     if (consoleCF.dwEffects & CFE_ITALIC)\r
4433         lf.lfItalic = TRUE;\r
4434     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4435         lf.lfStrikeOut = TRUE;\r
4436     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4437         lf.lfUnderline = TRUE;\r
4438     hfont = CreateFontIndirect(&lf);\r
4439     hold_font = SelectObject(hdc, hfont);\r
4440     GetTextMetrics(hdc, &tm);\r
4441     SelectObject(hdc, hold_font);\r
4442     DeleteObject(hfont);\r
4443     ReleaseDC(hText, hdc);\r
4444 \r
4445     // get the rectangle\r
4446     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4447 \r
4448     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4449 }\r
4450 \r
4451 void UpdateICSWidth(HWND hText)\r
4452 {\r
4453     LONG old_width, new_width;\r
4454 \r
4455     new_width = get_term_width(hText, FALSE);\r
4456     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4457     if (new_width != old_width)\r
4458     {\r
4459         ics_update_width(new_width);\r
4460         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4461     }\r
4462 }\r
4463 \r
4464 VOID\r
4465 ChangedConsoleFont()\r
4466 {\r
4467   CHARFORMAT cfmt;\r
4468   CHARRANGE tmpsel, sel;\r
4469   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4470   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4471   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4472   PARAFORMAT paraf;\r
4473 \r
4474   cfmt.cbSize = sizeof(CHARFORMAT);\r
4475   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4476     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4477                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4478   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4479    * size.  This was undocumented in the version of MSVC++ that I had\r
4480    * when I wrote the code, but is apparently documented now.\r
4481    */\r
4482   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4483   cfmt.bCharSet = f->lf.lfCharSet;\r
4484   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4485   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4486   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4487   /* Why are the following seemingly needed too? */\r
4488   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4489   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4490   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4491   tmpsel.cpMin = 0;\r
4492   tmpsel.cpMax = -1; /*999999?*/\r
4493   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4494   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4495   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4496    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4497    */\r
4498   paraf.cbSize = sizeof(paraf);\r
4499   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4500   paraf.dxStartIndent = 0;\r
4501   paraf.dxOffset = WRAP_INDENT;\r
4502   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4503   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4504   UpdateICSWidth(hText);\r
4505 }\r
4506 \r
4507 /*---------------------------------------------------------------------------*\\r
4508  *\r
4509  * Window Proc for main window\r
4510  *\r
4511 \*---------------------------------------------------------------------------*/\r
4512 \r
4513 /* Process messages for main window, etc. */\r
4514 LRESULT CALLBACK\r
4515 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4516 {\r
4517   FARPROC lpProc;\r
4518   int wmId, wmEvent;\r
4519   char *defName;\r
4520   FILE *f;\r
4521   UINT number;\r
4522   char fileTitle[MSG_SIZ];\r
4523   char buf[MSG_SIZ];\r
4524   static SnapData sd;\r
4525 \r
4526   switch (message) {\r
4527 \r
4528   case WM_PAINT: /* message: repaint portion of window */\r
4529     PaintProc(hwnd);\r
4530     break;\r
4531 \r
4532   case WM_ERASEBKGND:\r
4533     if (IsIconic(hwnd)) {\r
4534       /* Cheat; change the message */\r
4535       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4536     } else {\r
4537       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4538     }\r
4539     break;\r
4540 \r
4541   case WM_LBUTTONDOWN:\r
4542   case WM_MBUTTONDOWN:\r
4543   case WM_RBUTTONDOWN:\r
4544   case WM_LBUTTONUP:\r
4545   case WM_MBUTTONUP:\r
4546   case WM_RBUTTONUP:\r
4547   case WM_MOUSEMOVE:\r
4548   case WM_MOUSEWHEEL:\r
4549     MouseEvent(hwnd, message, wParam, lParam);\r
4550     break;\r
4551 \r
4552   JAWS_KB_NAVIGATION\r
4553 \r
4554   case WM_CHAR:\r
4555     \r
4556     JAWS_ALT_INTERCEPT\r
4557 \r
4558     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4559         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4560         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4561         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4562         SetFocus(h);\r
4563         SendMessage(h, message, wParam, lParam);\r
4564     } else if(lParam != KF_REPEAT) {\r
4565         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4566                 PopUpMoveDialog((char)wParam);\r
4567         } else if((char)wParam == 003) CopyGameToClipboard();\r
4568          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4569     }\r
4570 \r
4571     break;\r
4572 \r
4573   case WM_PALETTECHANGED:\r
4574     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4575       int nnew;\r
4576       HDC hdc = GetDC(hwndMain);\r
4577       SelectPalette(hdc, hPal, TRUE);\r
4578       nnew = RealizePalette(hdc);\r
4579       if (nnew > 0) {\r
4580         paletteChanged = TRUE;\r
4581         InvalidateRect(hwnd, &boardRect, FALSE);\r
4582       }\r
4583       ReleaseDC(hwnd, hdc);\r
4584     }\r
4585     break;\r
4586 \r
4587   case WM_QUERYNEWPALETTE:\r
4588     if (!appData.monoMode /*&& paletteChanged*/) {\r
4589       int nnew;\r
4590       HDC hdc = GetDC(hwndMain);\r
4591       paletteChanged = FALSE;\r
4592       SelectPalette(hdc, hPal, FALSE);\r
4593       nnew = RealizePalette(hdc);\r
4594       if (nnew > 0) {\r
4595         InvalidateRect(hwnd, &boardRect, FALSE);\r
4596       }\r
4597       ReleaseDC(hwnd, hdc);\r
4598       return TRUE;\r
4599     }\r
4600     return FALSE;\r
4601 \r
4602   case WM_COMMAND: /* message: command from application menu */\r
4603     wmId    = LOWORD(wParam);\r
4604     wmEvent = HIWORD(wParam);\r
4605 \r
4606     switch (wmId) {\r
4607     case IDM_NewGame:\r
4608       ResetGameEvent();\r
4609       SAY("new game enter a move to play against the computer with white");\r
4610       break;\r
4611 \r
4612     case IDM_NewGameFRC:\r
4613       if( NewGameFRC() == 0 ) {\r
4614         ResetGameEvent();\r
4615       }\r
4616       break;\r
4617 \r
4618     case IDM_NewVariant:\r
4619       NewVariantPopup(hwnd);\r
4620       break;\r
4621 \r
4622     case IDM_LoadGame:\r
4623       LoadGameDialog(hwnd, _("Load Game from File"));\r
4624       break;\r
4625 \r
4626     case IDM_LoadNextGame:\r
4627       ReloadGame(1);\r
4628       break;\r
4629 \r
4630     case IDM_LoadPrevGame:\r
4631       ReloadGame(-1);\r
4632       break;\r
4633 \r
4634     case IDM_ReloadGame:\r
4635       ReloadGame(0);\r
4636       break;\r
4637 \r
4638     case IDM_LoadPosition:\r
4639       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4640         Reset(FALSE, TRUE);\r
4641       }\r
4642       number = 1;\r
4643       f = OpenFileDialog(hwnd, "rb", "",\r
4644                          appData.oldSaveStyle ? "pos" : "fen",\r
4645                          POSITION_FILT,\r
4646                          _("Load Position from File"), &number, fileTitle, NULL);\r
4647       if (f != NULL) {\r
4648         LoadPosition(f, number, fileTitle);\r
4649       }\r
4650       break;\r
4651 \r
4652     case IDM_LoadNextPosition:\r
4653       ReloadPosition(1);\r
4654       break;\r
4655 \r
4656     case IDM_LoadPrevPosition:\r
4657       ReloadPosition(-1);\r
4658       break;\r
4659 \r
4660     case IDM_ReloadPosition:\r
4661       ReloadPosition(0);\r
4662       break;\r
4663 \r
4664     case IDM_SaveGame:\r
4665       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4666       f = OpenFileDialog(hwnd, "a", defName,\r
4667                          appData.oldSaveStyle ? "gam" : "pgn",\r
4668                          GAME_FILT,\r
4669                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4670       if (f != NULL) {\r
4671         SaveGame(f, 0, "");\r
4672       }\r
4673       break;\r
4674 \r
4675     case IDM_SavePosition:\r
4676       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4677       f = OpenFileDialog(hwnd, "a", defName,\r
4678                          appData.oldSaveStyle ? "pos" : "fen",\r
4679                          POSITION_FILT,\r
4680                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4681       if (f != NULL) {\r
4682         SavePosition(f, 0, "");\r
4683       }\r
4684       break;\r
4685 \r
4686     case IDM_SaveDiagram:\r
4687       defName = "diagram";\r
4688       f = OpenFileDialog(hwnd, "wb", defName,\r
4689                          "bmp",\r
4690                          DIAGRAM_FILT,\r
4691                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4692       if (f != NULL) {\r
4693         SaveDiagram(f);\r
4694       }\r
4695       break;\r
4696 \r
4697     case IDM_CopyGame:\r
4698       CopyGameToClipboard();\r
4699       break;\r
4700 \r
4701     case IDM_PasteGame:\r
4702       PasteGameFromClipboard();\r
4703       break;\r
4704 \r
4705     case IDM_CopyGameListToClipboard:\r
4706       CopyGameListToClipboard();\r
4707       break;\r
4708 \r
4709     /* [AS] Autodetect FEN or PGN data */\r
4710     case IDM_PasteAny:\r
4711       PasteGameOrFENFromClipboard();\r
4712       break;\r
4713 \r
4714     /* [AS] Move history */\r
4715     case IDM_ShowMoveHistory:\r
4716         if( MoveHistoryIsUp() ) {\r
4717             MoveHistoryPopDown();\r
4718         }\r
4719         else {\r
4720             MoveHistoryPopUp();\r
4721         }\r
4722         break;\r
4723 \r
4724     /* [AS] Eval graph */\r
4725     case IDM_ShowEvalGraph:\r
4726         if( EvalGraphIsUp() ) {\r
4727             EvalGraphPopDown();\r
4728         }\r
4729         else {\r
4730             EvalGraphPopUp();\r
4731             SetFocus(hwndMain);\r
4732         }\r
4733         break;\r
4734 \r
4735     /* [AS] Engine output */\r
4736     case IDM_ShowEngineOutput:\r
4737         if( EngineOutputIsUp() ) {\r
4738             EngineOutputPopDown();\r
4739         }\r
4740         else {\r
4741             EngineOutputPopUp();\r
4742         }\r
4743         break;\r
4744 \r
4745     /* [AS] User adjudication */\r
4746     case IDM_UserAdjudication_White:\r
4747         UserAdjudicationEvent( +1 );\r
4748         break;\r
4749 \r
4750     case IDM_UserAdjudication_Black:\r
4751         UserAdjudicationEvent( -1 );\r
4752         break;\r
4753 \r
4754     case IDM_UserAdjudication_Draw:\r
4755         UserAdjudicationEvent( 0 );\r
4756         break;\r
4757 \r
4758     /* [AS] Game list options dialog */\r
4759     case IDM_GameListOptions:\r
4760       GameListOptions();\r
4761       break;\r
4762 \r
4763     case IDM_NewChat:\r
4764       ChatPopUp(NULL);\r
4765       break;\r
4766 \r
4767     case IDM_CopyPosition:\r
4768       CopyFENToClipboard();\r
4769       break;\r
4770 \r
4771     case IDM_PastePosition:\r
4772       PasteFENFromClipboard();\r
4773       break;\r
4774 \r
4775     case IDM_MailMove:\r
4776       MailMoveEvent();\r
4777       break;\r
4778 \r
4779     case IDM_ReloadCMailMsg:\r
4780       Reset(TRUE, TRUE);\r
4781       ReloadCmailMsgEvent(FALSE);\r
4782       break;\r
4783 \r
4784     case IDM_Minimize:\r
4785       ShowWindow(hwnd, SW_MINIMIZE);\r
4786       break;\r
4787 \r
4788     case IDM_Exit:\r
4789       ExitEvent(0);\r
4790       break;\r
4791 \r
4792     case IDM_MachineWhite:\r
4793       MachineWhiteEvent();\r
4794       /*\r
4795        * refresh the tags dialog only if it's visible\r
4796        */\r
4797       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4798           char *tags;\r
4799           tags = PGNTags(&gameInfo);\r
4800           TagsPopUp(tags, CmailMsg());\r
4801           free(tags);\r
4802       }\r
4803       SAY("computer starts playing white");\r
4804       break;\r
4805 \r
4806     case IDM_MachineBlack:\r
4807       MachineBlackEvent();\r
4808       /*\r
4809        * refresh the tags dialog only if it's visible\r
4810        */\r
4811       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4812           char *tags;\r
4813           tags = PGNTags(&gameInfo);\r
4814           TagsPopUp(tags, CmailMsg());\r
4815           free(tags);\r
4816       }\r
4817       SAY("computer starts playing black");\r
4818       break;\r
4819 \r
4820     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4821       if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting\r
4822       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4823       appData.matchGames = appData.defaultMatchGames;\r
4824       matchGame = 1;\r
4825 \r
4826     case IDM_TwoMachines:\r
4827       TwoMachinesEvent();\r
4828       /*\r
4829        * refresh the tags dialog only if it's visible\r
4830        */\r
4831       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4832           char *tags;\r
4833           tags = PGNTags(&gameInfo);\r
4834           TagsPopUp(tags, CmailMsg());\r
4835           free(tags);\r
4836       }\r
4837       SAY("computer starts playing both sides");\r
4838       break;\r
4839 \r
4840     case IDM_AnalysisMode:\r
4841       if (!first.analysisSupport) {\r
4842         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4843         DisplayError(buf, 0);\r
4844       } else {\r
4845         SAY("analyzing current position");\r
4846         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4847         if (appData.icsActive) {\r
4848                if (gameMode != IcsObserving) {\r
4849                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4850                        DisplayError(buf, 0);\r
4851                        /* secure check */\r
4852                        if (appData.icsEngineAnalyze) {\r
4853                                if (appData.debugMode) \r
4854                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4855                                ExitAnalyzeMode();\r
4856                                ModeHighlight();\r
4857                                break;\r
4858                        }\r
4859                        break;\r
4860                } else {\r
4861                        /* if enable, user want disable icsEngineAnalyze */\r
4862                        if (appData.icsEngineAnalyze) {\r
4863                                ExitAnalyzeMode();\r
4864                                ModeHighlight();\r
4865                                break;\r
4866                        }\r
4867                        appData.icsEngineAnalyze = TRUE;\r
4868                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4869                }\r
4870         } \r
4871         if (!appData.showThinking) ToggleShowThinking();\r
4872         AnalyzeModeEvent();\r
4873       }\r
4874       break;\r
4875 \r
4876     case IDM_AnalyzeFile:\r
4877       if (!first.analysisSupport) {\r
4878         char buf[MSG_SIZ];\r
4879           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4880         DisplayError(buf, 0);\r
4881       } else {\r
4882         if (!appData.showThinking) ToggleShowThinking();\r
4883         AnalyzeFileEvent();\r
4884         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4885         AnalysisPeriodicEvent(1);\r
4886       }\r
4887       break;\r
4888 \r
4889     case IDM_IcsClient:\r
4890       IcsClientEvent();\r
4891       break;\r
4892 \r
4893     case IDM_EditGame:\r
4894       EditGameEvent();\r
4895       SAY("edit game");\r
4896       break;\r
4897 \r
4898     case IDM_EditPosition:\r
4899       EditPositionEvent();\r
4900       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4901       break;\r
4902 \r
4903     case IDM_Training:\r
4904       TrainingEvent();\r
4905       break;\r
4906 \r
4907     case IDM_ShowGameList:\r
4908       ShowGameListProc();\r
4909       break;\r
4910 \r
4911     case IDM_EditTags:\r
4912       EditTagsProc();\r
4913       break;\r
4914 \r
4915     case IDM_EditComment:\r
4916       if (commentUp && editComment) {\r
4917         CommentPopDown();\r
4918       } else {\r
4919         EditCommentEvent();\r
4920       }\r
4921       break;\r
4922 \r
4923     case IDM_Pause:\r
4924       PauseEvent();\r
4925       break;\r
4926 \r
4927     case IDM_Accept:\r
4928       AcceptEvent();\r
4929       break;\r
4930 \r
4931     case IDM_Decline:\r
4932       DeclineEvent();\r
4933       break;\r
4934 \r
4935     case IDM_Rematch:\r
4936       RematchEvent();\r
4937       break;\r
4938 \r
4939     case IDM_CallFlag:\r
4940       CallFlagEvent();\r
4941       break;\r
4942 \r
4943     case IDM_Draw:\r
4944       DrawEvent();\r
4945       break;\r
4946 \r
4947     case IDM_Adjourn:\r
4948       AdjournEvent();\r
4949       break;\r
4950 \r
4951     case IDM_Abort:\r
4952       AbortEvent();\r
4953       break;\r
4954 \r
4955     case IDM_Resign:\r
4956       ResignEvent();\r
4957       break;\r
4958 \r
4959     case IDM_StopObserving:\r
4960       StopObservingEvent();\r
4961       break;\r
4962 \r
4963     case IDM_StopExamining:\r
4964       StopExaminingEvent();\r
4965       break;\r
4966 \r
4967     case IDM_Upload:\r
4968       UploadGameEvent();\r
4969       break;\r
4970 \r
4971     case IDM_TypeInMove:\r
4972       PopUpMoveDialog('\000');\r
4973       break;\r
4974 \r
4975     case IDM_TypeInName:\r
4976       PopUpNameDialog('\000');\r
4977       break;\r
4978 \r
4979     case IDM_Backward:\r
4980       BackwardEvent();\r
4981       SetFocus(hwndMain);\r
4982       break;\r
4983 \r
4984     JAWS_MENU_ITEMS\r
4985 \r
4986     case IDM_Forward:\r
4987       ForwardEvent();\r
4988       SetFocus(hwndMain);\r
4989       break;\r
4990 \r
4991     case IDM_ToStart:\r
4992       ToStartEvent();\r
4993       SetFocus(hwndMain);\r
4994       break;\r
4995 \r
4996     case IDM_ToEnd:\r
4997       ToEndEvent();\r
4998       SetFocus(hwndMain);\r
4999       break;\r
5000 \r
5001     case IDM_Revert:\r
5002       RevertEvent(FALSE);\r
5003       break;\r
5004 \r
5005     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5006       RevertEvent(TRUE);\r
5007       break;\r
5008 \r
5009     case IDM_TruncateGame:\r
5010       TruncateGameEvent();\r
5011       break;\r
5012 \r
5013     case IDM_MoveNow:\r
5014       MoveNowEvent();\r
5015       break;\r
5016 \r
5017     case IDM_RetractMove:\r
5018       RetractMoveEvent();\r
5019       break;\r
5020 \r
5021     case IDM_FlipView:\r
5022       flipView = !flipView;\r
5023       DrawPosition(FALSE, NULL);\r
5024       break;\r
5025 \r
5026     case IDM_FlipClock:\r
5027       flipClock = !flipClock;\r
5028       DisplayBothClocks();\r
5029       DrawPosition(FALSE, NULL);\r
5030       break;\r
5031 \r
5032     case IDM_MuteSounds:\r
5033       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5034       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5035                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5036       break;\r
5037 \r
5038     case IDM_GeneralOptions:\r
5039       GeneralOptionsPopup(hwnd);\r
5040       DrawPosition(TRUE, NULL);\r
5041       break;\r
5042 \r
5043     case IDM_BoardOptions:\r
5044       BoardOptionsPopup(hwnd);\r
5045       break;\r
5046 \r
5047     case IDM_EnginePlayOptions:\r
5048       EnginePlayOptionsPopup(hwnd);\r
5049       break;\r
5050 \r
5051     case IDM_Engine1Options:\r
5052       EngineOptionsPopup(hwnd, &first);\r
5053       break;\r
5054 \r
5055     case IDM_Engine2Options:\r
5056       savedHwnd = hwnd;\r
5057       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5058       EngineOptionsPopup(hwnd, &second);\r
5059       break;\r
5060 \r
5061     case IDM_OptionsUCI:\r
5062       UciOptionsPopup(hwnd);\r
5063       break;\r
5064 \r
5065     case IDM_IcsOptions:\r
5066       IcsOptionsPopup(hwnd);\r
5067       break;\r
5068 \r
5069     case IDM_Fonts:\r
5070       FontsOptionsPopup(hwnd);\r
5071       break;\r
5072 \r
5073     case IDM_Sounds:\r
5074       SoundOptionsPopup(hwnd);\r
5075       break;\r
5076 \r
5077     case IDM_CommPort:\r
5078       CommPortOptionsPopup(hwnd);\r
5079       break;\r
5080 \r
5081     case IDM_LoadOptions:\r
5082       LoadOptionsPopup(hwnd);\r
5083       break;\r
5084 \r
5085     case IDM_SaveOptions:\r
5086       SaveOptionsPopup(hwnd);\r
5087       break;\r
5088 \r
5089     case IDM_TimeControl:\r
5090       TimeControlOptionsPopup(hwnd);\r
5091       break;\r
5092 \r
5093     case IDM_SaveSettings:\r
5094       SaveSettings(settingsFileName);\r
5095       break;\r
5096 \r
5097     case IDM_SaveSettingsOnExit:\r
5098       saveSettingsOnExit = !saveSettingsOnExit;\r
5099       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5100                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5101                                          MF_CHECKED : MF_UNCHECKED));\r
5102       break;\r
5103 \r
5104     case IDM_Hint:\r
5105       HintEvent();\r
5106       break;\r
5107 \r
5108     case IDM_Book:\r
5109       BookEvent();\r
5110       break;\r
5111 \r
5112     case IDM_AboutGame:\r
5113       AboutGameEvent();\r
5114       break;\r
5115 \r
5116     case IDM_Debug:\r
5117       appData.debugMode = !appData.debugMode;\r
5118       if (appData.debugMode) {\r
5119         char dir[MSG_SIZ];\r
5120         GetCurrentDirectory(MSG_SIZ, dir);\r
5121         SetCurrentDirectory(installDir);\r
5122         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5123         SetCurrentDirectory(dir);\r
5124         setbuf(debugFP, NULL);\r
5125       } else {\r
5126         fclose(debugFP);\r
5127         debugFP = NULL;\r
5128       }\r
5129       break;\r
5130 \r
5131     case IDM_HELPCONTENTS:\r
5132       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5133           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5134           MessageBox (GetFocus(),\r
5135                     _("Unable to activate help"),\r
5136                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5137       }\r
5138       break;\r
5139 \r
5140     case IDM_HELPSEARCH:\r
5141         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5142             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5143         MessageBox (GetFocus(),\r
5144                     _("Unable to activate help"),\r
5145                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5146       }\r
5147       break;\r
5148 \r
5149     case IDM_HELPHELP:\r
5150       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5151         MessageBox (GetFocus(),\r
5152                     _("Unable to activate help"),\r
5153                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5154       }\r
5155       break;\r
5156 \r
5157     case IDM_ABOUT:\r
5158       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5159       DialogBox(hInst, \r
5160         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5161         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5162       FreeProcInstance(lpProc);\r
5163       break;\r
5164 \r
5165     case IDM_DirectCommand1:\r
5166       AskQuestionEvent(_("Direct Command"),\r
5167                        _("Send to chess program:"), "", "1");\r
5168       break;\r
5169     case IDM_DirectCommand2:\r
5170       AskQuestionEvent(_("Direct Command"),\r
5171                        _("Send to second chess program:"), "", "2");\r
5172       break;\r
5173 \r
5174     case EP_WhitePawn:\r
5175       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5176       fromX = fromY = -1;\r
5177       break;\r
5178 \r
5179     case EP_WhiteKnight:\r
5180       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5181       fromX = fromY = -1;\r
5182       break;\r
5183 \r
5184     case EP_WhiteBishop:\r
5185       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5186       fromX = fromY = -1;\r
5187       break;\r
5188 \r
5189     case EP_WhiteRook:\r
5190       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5191       fromX = fromY = -1;\r
5192       break;\r
5193 \r
5194     case EP_WhiteQueen:\r
5195       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5196       fromX = fromY = -1;\r
5197       break;\r
5198 \r
5199     case EP_WhiteFerz:\r
5200       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5201       fromX = fromY = -1;\r
5202       break;\r
5203 \r
5204     case EP_WhiteWazir:\r
5205       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5206       fromX = fromY = -1;\r
5207       break;\r
5208 \r
5209     case EP_WhiteAlfil:\r
5210       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5211       fromX = fromY = -1;\r
5212       break;\r
5213 \r
5214     case EP_WhiteCannon:\r
5215       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5216       fromX = fromY = -1;\r
5217       break;\r
5218 \r
5219     case EP_WhiteCardinal:\r
5220       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5221       fromX = fromY = -1;\r
5222       break;\r
5223 \r
5224     case EP_WhiteMarshall:\r
5225       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5226       fromX = fromY = -1;\r
5227       break;\r
5228 \r
5229     case EP_WhiteKing:\r
5230       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5231       fromX = fromY = -1;\r
5232       break;\r
5233 \r
5234     case EP_BlackPawn:\r
5235       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5236       fromX = fromY = -1;\r
5237       break;\r
5238 \r
5239     case EP_BlackKnight:\r
5240       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5241       fromX = fromY = -1;\r
5242       break;\r
5243 \r
5244     case EP_BlackBishop:\r
5245       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5246       fromX = fromY = -1;\r
5247       break;\r
5248 \r
5249     case EP_BlackRook:\r
5250       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5251       fromX = fromY = -1;\r
5252       break;\r
5253 \r
5254     case EP_BlackQueen:\r
5255       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5256       fromX = fromY = -1;\r
5257       break;\r
5258 \r
5259     case EP_BlackFerz:\r
5260       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5261       fromX = fromY = -1;\r
5262       break;\r
5263 \r
5264     case EP_BlackWazir:\r
5265       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5266       fromX = fromY = -1;\r
5267       break;\r
5268 \r
5269     case EP_BlackAlfil:\r
5270       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5271       fromX = fromY = -1;\r
5272       break;\r
5273 \r
5274     case EP_BlackCannon:\r
5275       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5276       fromX = fromY = -1;\r
5277       break;\r
5278 \r
5279     case EP_BlackCardinal:\r
5280       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5281       fromX = fromY = -1;\r
5282       break;\r
5283 \r
5284     case EP_BlackMarshall:\r
5285       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5286       fromX = fromY = -1;\r
5287       break;\r
5288 \r
5289     case EP_BlackKing:\r
5290       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5291       fromX = fromY = -1;\r
5292       break;\r
5293 \r
5294     case EP_EmptySquare:\r
5295       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5296       fromX = fromY = -1;\r
5297       break;\r
5298 \r
5299     case EP_ClearBoard:\r
5300       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5301       fromX = fromY = -1;\r
5302       break;\r
5303 \r
5304     case EP_White:\r
5305       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5306       fromX = fromY = -1;\r
5307       break;\r
5308 \r
5309     case EP_Black:\r
5310       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5311       fromX = fromY = -1;\r
5312       break;\r
5313 \r
5314     case EP_Promote:\r
5315       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5316       fromX = fromY = -1;\r
5317       break;\r
5318 \r
5319     case EP_Demote:\r
5320       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5321       fromX = fromY = -1;\r
5322       break;\r
5323 \r
5324     case DP_Pawn:\r
5325       DropMenuEvent(WhitePawn, fromX, fromY);\r
5326       fromX = fromY = -1;\r
5327       break;\r
5328 \r
5329     case DP_Knight:\r
5330       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5331       fromX = fromY = -1;\r
5332       break;\r
5333 \r
5334     case DP_Bishop:\r
5335       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5336       fromX = fromY = -1;\r
5337       break;\r
5338 \r
5339     case DP_Rook:\r
5340       DropMenuEvent(WhiteRook, fromX, fromY);\r
5341       fromX = fromY = -1;\r
5342       break;\r
5343 \r
5344     case DP_Queen:\r
5345       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5346       fromX = fromY = -1;\r
5347       break;\r
5348 \r
5349     case IDM_English:\r
5350       barbaric = 0;\r
5351       TranslateMenus(0);\r
5352       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5353       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5354       lastChecked = wmId;\r
5355       break;\r
5356 \r
5357     default:\r
5358       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5359           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5360           TranslateMenus(0);\r
5361           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5362           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5363           lastChecked = wmId;\r
5364           break;\r
5365       }\r
5366       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5367     }\r
5368     break;\r
5369 \r
5370   case WM_TIMER:\r
5371     switch (wParam) {\r
5372     case CLOCK_TIMER_ID:\r
5373       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5374       clockTimerEvent = 0;\r
5375       DecrementClocks(); /* call into back end */\r
5376       break;\r
5377     case LOAD_GAME_TIMER_ID:\r
5378       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5379       loadGameTimerEvent = 0;\r
5380       AutoPlayGameLoop(); /* call into back end */\r
5381       break;\r
5382     case ANALYSIS_TIMER_ID:\r
5383       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5384                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5385         AnalysisPeriodicEvent(0);\r
5386       } else {\r
5387         KillTimer(hwnd, analysisTimerEvent);\r
5388         analysisTimerEvent = 0;\r
5389       }\r
5390       break;\r
5391     case DELAYED_TIMER_ID:\r
5392       KillTimer(hwnd, delayedTimerEvent);\r
5393       delayedTimerEvent = 0;\r
5394       delayedTimerCallback();\r
5395       break;\r
5396     }\r
5397     break;\r
5398 \r
5399   case WM_USER_Input:\r
5400     InputEvent(hwnd, message, wParam, lParam);\r
5401     break;\r
5402 \r
5403   /* [AS] Also move "attached" child windows */\r
5404   case WM_WINDOWPOSCHANGING:\r
5405 \r
5406     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5407         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5408 \r
5409         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5410             /* Window is moving */\r
5411             RECT rcMain;\r
5412 \r
5413 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5414             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5415             rcMain.right  = wpMain.x + wpMain.width;\r
5416             rcMain.top    = wpMain.y;\r
5417             rcMain.bottom = wpMain.y + wpMain.height;\r
5418             \r
5419             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5420             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5421             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5422             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5423             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5424             wpMain.x = lpwp->x;\r
5425             wpMain.y = lpwp->y;\r
5426         }\r
5427     }\r
5428     break;\r
5429 \r
5430   /* [AS] Snapping */\r
5431   case WM_ENTERSIZEMOVE:\r
5432     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5433     if (hwnd == hwndMain) {\r
5434       doingSizing = TRUE;\r
5435       lastSizing = 0;\r
5436     }\r
5437     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5438     break;\r
5439 \r
5440   case WM_SIZING:\r
5441     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5442     if (hwnd == hwndMain) {\r
5443       lastSizing = wParam;\r
5444     }\r
5445     break;\r
5446 \r
5447   case WM_MOVING:\r
5448     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5449       return OnMoving( &sd, hwnd, wParam, lParam );\r
5450 \r
5451   case WM_EXITSIZEMOVE:\r
5452     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5453     if (hwnd == hwndMain) {\r
5454       RECT client;\r
5455       doingSizing = FALSE;\r
5456       InvalidateRect(hwnd, &boardRect, FALSE);\r
5457       GetClientRect(hwnd, &client);\r
5458       ResizeBoard(client.right, client.bottom, lastSizing);\r
5459       lastSizing = 0;\r
5460       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5461     }\r
5462     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5463     break;\r
5464 \r
5465   case WM_DESTROY: /* message: window being destroyed */\r
5466     PostQuitMessage(0);\r
5467     break;\r
5468 \r
5469   case WM_CLOSE:\r
5470     if (hwnd == hwndMain) {\r
5471       ExitEvent(0);\r
5472     }\r
5473     break;\r
5474 \r
5475   default:      /* Passes it on if unprocessed */\r
5476     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5477   }\r
5478   return 0;\r
5479 }\r
5480 \r
5481 /*---------------------------------------------------------------------------*\\r
5482  *\r
5483  * Misc utility routines\r
5484  *\r
5485 \*---------------------------------------------------------------------------*/\r
5486 \r
5487 /*\r
5488  * Decent random number generator, at least not as bad as Windows\r
5489  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5490  */\r
5491 unsigned int randstate;\r
5492 \r
5493 int\r
5494 myrandom(void)\r
5495 {\r
5496   randstate = randstate * 1664525 + 1013904223;\r
5497   return (int) randstate & 0x7fffffff;\r
5498 }\r
5499 \r
5500 void\r
5501 mysrandom(unsigned int seed)\r
5502 {\r
5503   randstate = seed;\r
5504 }\r
5505 \r
5506 \r
5507 /* \r
5508  * returns TRUE if user selects a different color, FALSE otherwise \r
5509  */\r
5510 \r
5511 BOOL\r
5512 ChangeColor(HWND hwnd, COLORREF *which)\r
5513 {\r
5514   static BOOL firstTime = TRUE;\r
5515   static DWORD customColors[16];\r
5516   CHOOSECOLOR cc;\r
5517   COLORREF newcolor;\r
5518   int i;\r
5519   ColorClass ccl;\r
5520 \r
5521   if (firstTime) {\r
5522     /* Make initial colors in use available as custom colors */\r
5523     /* Should we put the compiled-in defaults here instead? */\r
5524     i = 0;\r
5525     customColors[i++] = lightSquareColor & 0xffffff;\r
5526     customColors[i++] = darkSquareColor & 0xffffff;\r
5527     customColors[i++] = whitePieceColor & 0xffffff;\r
5528     customColors[i++] = blackPieceColor & 0xffffff;\r
5529     customColors[i++] = highlightSquareColor & 0xffffff;\r
5530     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5531 \r
5532     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5533       customColors[i++] = textAttribs[ccl].color;\r
5534     }\r
5535     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5536     firstTime = FALSE;\r
5537   }\r
5538 \r
5539   cc.lStructSize = sizeof(cc);\r
5540   cc.hwndOwner = hwnd;\r
5541   cc.hInstance = NULL;\r
5542   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5543   cc.lpCustColors = (LPDWORD) customColors;\r
5544   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5545 \r
5546   if (!ChooseColor(&cc)) return FALSE;\r
5547 \r
5548   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5549   if (newcolor == *which) return FALSE;\r
5550   *which = newcolor;\r
5551   return TRUE;\r
5552 \r
5553   /*\r
5554   InitDrawingColors();\r
5555   InvalidateRect(hwnd, &boardRect, FALSE);\r
5556   */\r
5557 }\r
5558 \r
5559 BOOLEAN\r
5560 MyLoadSound(MySound *ms)\r
5561 {\r
5562   BOOL ok = FALSE;\r
5563   struct stat st;\r
5564   FILE *f;\r
5565 \r
5566   if (ms->data) free(ms->data);\r
5567   ms->data = NULL;\r
5568 \r
5569   switch (ms->name[0]) {\r
5570   case NULLCHAR:\r
5571     /* Silence */\r
5572     ok = TRUE;\r
5573     break;\r
5574   case '$':\r
5575     /* System sound from Control Panel.  Don't preload here. */\r
5576     ok = TRUE;\r
5577     break;\r
5578   case '!':\r
5579     if (ms->name[1] == NULLCHAR) {\r
5580       /* "!" alone = silence */\r
5581       ok = TRUE;\r
5582     } else {\r
5583       /* Builtin wave resource.  Error if not found. */\r
5584       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5585       if (h == NULL) break;\r
5586       ms->data = (void *)LoadResource(hInst, h);\r
5587       if (h == NULL) break;\r
5588       ok = TRUE;\r
5589     }\r
5590     break;\r
5591   default:\r
5592     /* .wav file.  Error if not found. */\r
5593     f = fopen(ms->name, "rb");\r
5594     if (f == NULL) break;\r
5595     if (fstat(fileno(f), &st) < 0) break;\r
5596     ms->data = malloc(st.st_size);\r
5597     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5598     fclose(f);\r
5599     ok = TRUE;\r
5600     break;\r
5601   }\r
5602   if (!ok) {\r
5603     char buf[MSG_SIZ];\r
5604       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5605     DisplayError(buf, GetLastError());\r
5606   }\r
5607   return ok;\r
5608 }\r
5609 \r
5610 BOOLEAN\r
5611 MyPlaySound(MySound *ms)\r
5612 {\r
5613   BOOLEAN ok = FALSE;\r
5614 \r
5615   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5616   switch (ms->name[0]) {\r
5617   case NULLCHAR:\r
5618         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5619     /* Silence */\r
5620     ok = TRUE;\r
5621     break;\r
5622   case '$':\r
5623     /* System sound from Control Panel (deprecated feature).\r
5624        "$" alone or an unset sound name gets default beep (still in use). */\r
5625     if (ms->name[1]) {\r
5626       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5627     }\r
5628     if (!ok) ok = MessageBeep(MB_OK);\r
5629     break; \r
5630   case '!':\r
5631     /* Builtin wave resource, or "!" alone for silence */\r
5632     if (ms->name[1]) {\r
5633       if (ms->data == NULL) return FALSE;\r
5634       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5635     } else {\r
5636       ok = TRUE;\r
5637     }\r
5638     break;\r
5639   default:\r
5640     /* .wav file.  Error if not found. */\r
5641     if (ms->data == NULL) return FALSE;\r
5642     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5643     break;\r
5644   }\r
5645   /* Don't print an error: this can happen innocently if the sound driver\r
5646      is busy; for instance, if another instance of WinBoard is playing\r
5647      a sound at about the same time. */\r
5648   return ok;\r
5649 }\r
5650 \r
5651 \r
5652 LRESULT CALLBACK\r
5653 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5654 {\r
5655   BOOL ok;\r
5656   OPENFILENAME *ofn;\r
5657   static UINT *number; /* gross that this is static */\r
5658 \r
5659   switch (message) {\r
5660   case WM_INITDIALOG: /* message: initialize dialog box */\r
5661     /* Center the dialog over the application window */\r
5662     ofn = (OPENFILENAME *) lParam;\r
5663     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5664       number = (UINT *) ofn->lCustData;\r
5665       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5666     } else {\r
5667       number = NULL;\r
5668     }\r
5669     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5670     Translate(hDlg, 1536);\r
5671     return FALSE;  /* Allow for further processing */\r
5672 \r
5673   case WM_COMMAND:\r
5674     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5675       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5676     }\r
5677     return FALSE;  /* Allow for further processing */\r
5678   }\r
5679   return FALSE;\r
5680 }\r
5681 \r
5682 UINT APIENTRY\r
5683 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5684 {\r
5685   static UINT *number;\r
5686   OPENFILENAME *ofname;\r
5687   OFNOTIFY *ofnot;\r
5688   switch (uiMsg) {\r
5689   case WM_INITDIALOG:\r
5690     Translate(hdlg, DLG_IndexNumber);\r
5691     ofname = (OPENFILENAME *)lParam;\r
5692     number = (UINT *)(ofname->lCustData);\r
5693     break;\r
5694   case WM_NOTIFY:\r
5695     ofnot = (OFNOTIFY *)lParam;\r
5696     if (ofnot->hdr.code == CDN_FILEOK) {\r
5697       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5698     }\r
5699     break;\r
5700   }\r
5701   return 0;\r
5702 }\r
5703 \r
5704 \r
5705 FILE *\r
5706 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5707                char *nameFilt, char *dlgTitle, UINT *number,\r
5708                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5709 {\r
5710   OPENFILENAME openFileName;\r
5711   char buf1[MSG_SIZ];\r
5712   FILE *f;\r
5713 \r
5714   if (fileName == NULL) fileName = buf1;\r
5715   if (defName == NULL) {\r
5716     safeStrCpy(fileName, "*.", 3 );\r
5717     strcat(fileName, defExt);\r
5718   } else {\r
5719     safeStrCpy(fileName, defName, MSG_SIZ );\r
5720   }\r
5721     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5722   if (number) *number = 0;\r
5723 \r
5724   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5725   openFileName.hwndOwner         = hwnd;\r
5726   openFileName.hInstance         = (HANDLE) hInst;\r
5727   openFileName.lpstrFilter       = nameFilt;\r
5728   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5729   openFileName.nMaxCustFilter    = 0L;\r
5730   openFileName.nFilterIndex      = 1L;\r
5731   openFileName.lpstrFile         = fileName;\r
5732   openFileName.nMaxFile          = MSG_SIZ;\r
5733   openFileName.lpstrFileTitle    = fileTitle;\r
5734   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5735   openFileName.lpstrInitialDir   = NULL;\r
5736   openFileName.lpstrTitle        = dlgTitle;\r
5737   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5738     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5739     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5740     | (oldDialog ? 0 : OFN_EXPLORER);\r
5741   openFileName.nFileOffset       = 0;\r
5742   openFileName.nFileExtension    = 0;\r
5743   openFileName.lpstrDefExt       = defExt;\r
5744   openFileName.lCustData         = (LONG) number;\r
5745   openFileName.lpfnHook          = oldDialog ?\r
5746     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5747   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5748 \r
5749   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5750                         GetOpenFileName(&openFileName)) {\r
5751     /* open the file */\r
5752     f = fopen(openFileName.lpstrFile, write);\r
5753     if (f == NULL) {\r
5754       MessageBox(hwnd, _("File open failed"), NULL,\r
5755                  MB_OK|MB_ICONEXCLAMATION);\r
5756       return NULL;\r
5757     }\r
5758   } else {\r
5759     int err = CommDlgExtendedError();\r
5760     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5761     return FALSE;\r
5762   }\r
5763   return f;\r
5764 }\r
5765 \r
5766 \r
5767 \r
5768 VOID APIENTRY\r
5769 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5770 {\r
5771   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5772 \r
5773   /*\r
5774    * Get the first pop-up menu in the menu template. This is the\r
5775    * menu that TrackPopupMenu displays.\r
5776    */\r
5777   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5778 \r
5779   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5780 \r
5781   /*\r
5782    * TrackPopup uses screen coordinates, so convert the\r
5783    * coordinates of the mouse click to screen coordinates.\r
5784    */\r
5785   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5786 \r
5787   /* Draw and track the floating pop-up menu. */\r
5788   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5789                  pt.x, pt.y, 0, hwnd, NULL);\r
5790 \r
5791   /* Destroy the menu.*/\r
5792   DestroyMenu(hmenu);\r
5793 }\r
5794    \r
5795 typedef struct {\r
5796   HWND hDlg, hText;\r
5797   int sizeX, sizeY, newSizeX, newSizeY;\r
5798   HDWP hdwp;\r
5799 } ResizeEditPlusButtonsClosure;\r
5800 \r
5801 BOOL CALLBACK\r
5802 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5803 {\r
5804   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5805   RECT rect;\r
5806   POINT pt;\r
5807 \r
5808   if (hChild == cl->hText) return TRUE;\r
5809   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5810   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5811   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5812   ScreenToClient(cl->hDlg, &pt);\r
5813   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5814     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5815   return TRUE;\r
5816 }\r
5817 \r
5818 /* Resize a dialog that has a (rich) edit field filling most of\r
5819    the top, with a row of buttons below */\r
5820 VOID\r
5821 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5822 {\r
5823   RECT rectText;\r
5824   int newTextHeight, newTextWidth;\r
5825   ResizeEditPlusButtonsClosure cl;\r
5826   \r
5827   /*if (IsIconic(hDlg)) return;*/\r
5828   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5829   \r
5830   cl.hdwp = BeginDeferWindowPos(8);\r
5831 \r
5832   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5833   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5834   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5835   if (newTextHeight < 0) {\r
5836     newSizeY += -newTextHeight;\r
5837     newTextHeight = 0;\r
5838   }\r
5839   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5840     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5841 \r
5842   cl.hDlg = hDlg;\r
5843   cl.hText = hText;\r
5844   cl.sizeX = sizeX;\r
5845   cl.sizeY = sizeY;\r
5846   cl.newSizeX = newSizeX;\r
5847   cl.newSizeY = newSizeY;\r
5848   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5849 \r
5850   EndDeferWindowPos(cl.hdwp);\r
5851 }\r
5852 \r
5853 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5854 {\r
5855     RECT    rChild, rParent;\r
5856     int     wChild, hChild, wParent, hParent;\r
5857     int     wScreen, hScreen, xNew, yNew;\r
5858     HDC     hdc;\r
5859 \r
5860     /* Get the Height and Width of the child window */\r
5861     GetWindowRect (hwndChild, &rChild);\r
5862     wChild = rChild.right - rChild.left;\r
5863     hChild = rChild.bottom - rChild.top;\r
5864 \r
5865     /* Get the Height and Width of the parent window */\r
5866     GetWindowRect (hwndParent, &rParent);\r
5867     wParent = rParent.right - rParent.left;\r
5868     hParent = rParent.bottom - rParent.top;\r
5869 \r
5870     /* Get the display limits */\r
5871     hdc = GetDC (hwndChild);\r
5872     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5873     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5874     ReleaseDC(hwndChild, hdc);\r
5875 \r
5876     /* Calculate new X position, then adjust for screen */\r
5877     xNew = rParent.left + ((wParent - wChild) /2);\r
5878     if (xNew < 0) {\r
5879         xNew = 0;\r
5880     } else if ((xNew+wChild) > wScreen) {\r
5881         xNew = wScreen - wChild;\r
5882     }\r
5883 \r
5884     /* Calculate new Y position, then adjust for screen */\r
5885     if( mode == 0 ) {\r
5886         yNew = rParent.top  + ((hParent - hChild) /2);\r
5887     }\r
5888     else {\r
5889         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5890     }\r
5891 \r
5892     if (yNew < 0) {\r
5893         yNew = 0;\r
5894     } else if ((yNew+hChild) > hScreen) {\r
5895         yNew = hScreen - hChild;\r
5896     }\r
5897 \r
5898     /* Set it, and return */\r
5899     return SetWindowPos (hwndChild, NULL,\r
5900                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5901 }\r
5902 \r
5903 /* Center one window over another */\r
5904 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5905 {\r
5906     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5907 }\r
5908 \r
5909 /*---------------------------------------------------------------------------*\\r
5910  *\r
5911  * Startup Dialog functions\r
5912  *\r
5913 \*---------------------------------------------------------------------------*/\r
5914 void\r
5915 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5916 {\r
5917   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5918 \r
5919   while (*cd != NULL) {\r
5920     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5921     cd++;\r
5922   }\r
5923 }\r
5924 \r
5925 void\r
5926 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5927 {\r
5928   char buf1[MAX_ARG_LEN];\r
5929   int len;\r
5930 \r
5931   if (str[0] == '@') {\r
5932     FILE* f = fopen(str + 1, "r");\r
5933     if (f == NULL) {\r
5934       DisplayFatalError(str + 1, errno, 2);\r
5935       return;\r
5936     }\r
5937     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5938     fclose(f);\r
5939     buf1[len] = NULLCHAR;\r
5940     str = buf1;\r
5941   }\r
5942 \r
5943   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5944 \r
5945   for (;;) {\r
5946     char buf[MSG_SIZ];\r
5947     char *end = strchr(str, '\n');\r
5948     if (end == NULL) return;\r
5949     memcpy(buf, str, end - str);\r
5950     buf[end - str] = NULLCHAR;\r
5951     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5952     str = end + 1;\r
5953   }\r
5954 }\r
5955 \r
5956 void\r
5957 SetStartupDialogEnables(HWND hDlg)\r
5958 {\r
5959   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5960     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5961     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5962   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5963     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5964   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5965     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5966   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5967     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5968   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5969     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5970     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5971     IsDlgButtonChecked(hDlg, OPT_View));\r
5972 }\r
5973 \r
5974 char *\r
5975 QuoteForFilename(char *filename)\r
5976 {\r
5977   int dquote, space;\r
5978   dquote = strchr(filename, '"') != NULL;\r
5979   space = strchr(filename, ' ') != NULL;\r
5980   if (dquote || space) {\r
5981     if (dquote) {\r
5982       return "'";\r
5983     } else {\r
5984       return "\"";\r
5985     }\r
5986   } else {\r
5987     return "";\r
5988   }\r
5989 }\r
5990 \r
5991 VOID\r
5992 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5993 {\r
5994   char buf[MSG_SIZ];\r
5995   char *q;\r
5996 \r
5997   InitComboStringsFromOption(hwndCombo, nthnames);\r
5998   q = QuoteForFilename(nthcp);\r
5999     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6000   if (*nthdir != NULLCHAR) {\r
6001     q = QuoteForFilename(nthdir);\r
6002       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6003   }\r
6004   if (*nthcp == NULLCHAR) {\r
6005     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6006   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6007     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6008     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6009   }\r
6010 }\r
6011 \r
6012 LRESULT CALLBACK\r
6013 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6014 {\r
6015   char buf[MSG_SIZ];\r
6016   HANDLE hwndCombo;\r
6017   char *p;\r
6018 \r
6019   switch (message) {\r
6020   case WM_INITDIALOG:\r
6021     /* Center the dialog */\r
6022     CenterWindow (hDlg, GetDesktopWindow());\r
6023     Translate(hDlg, DLG_Startup);\r
6024     /* Initialize the dialog items */\r
6025     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6026                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6027                   firstChessProgramNames);\r
6028     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6029                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6030                   secondChessProgramNames);\r
6031     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6032     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6033       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6034     if (*appData.icsHelper != NULLCHAR) {\r
6035       char *q = QuoteForFilename(appData.icsHelper);\r
6036       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6037     }\r
6038     if (*appData.icsHost == NULLCHAR) {\r
6039       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6040       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6041     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6042       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6043       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6044     }\r
6045 \r
6046     if (appData.icsActive) {\r
6047       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6048     }\r
6049     else if (appData.noChessProgram) {\r
6050       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6051     }\r
6052     else {\r
6053       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6054     }\r
6055 \r
6056     SetStartupDialogEnables(hDlg);\r
6057     return TRUE;\r
6058 \r
6059   case WM_COMMAND:\r
6060     switch (LOWORD(wParam)) {\r
6061     case IDOK:\r
6062       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6063         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6064         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6065         p = buf;\r
6066         ParseArgs(StringGet, &p);\r
6067         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6068         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6069         p = buf;\r
6070         ParseArgs(StringGet, &p);\r
6071         appData.noChessProgram = FALSE;\r
6072         appData.icsActive = FALSE;\r
6073       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6074         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6075         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6076         p = buf;\r
6077         ParseArgs(StringGet, &p);\r
6078         if (appData.zippyPlay) {\r
6079           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6080           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6081           p = buf;\r
6082           ParseArgs(StringGet, &p);\r
6083         }\r
6084       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6085         appData.noChessProgram = TRUE;\r
6086         appData.icsActive = FALSE;\r
6087       } else {\r
6088         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6089                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6090         return TRUE;\r
6091       }\r
6092       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6093         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6094         p = buf;\r
6095         ParseArgs(StringGet, &p);\r
6096       }\r
6097       EndDialog(hDlg, TRUE);\r
6098       return TRUE;\r
6099 \r
6100     case IDCANCEL:\r
6101       ExitEvent(0);\r
6102       return TRUE;\r
6103 \r
6104     case IDM_HELPCONTENTS:\r
6105       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6106         MessageBox (GetFocus(),\r
6107                     _("Unable to activate help"),\r
6108                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6109       }\r
6110       break;\r
6111 \r
6112     default:\r
6113       SetStartupDialogEnables(hDlg);\r
6114       break;\r
6115     }\r
6116     break;\r
6117   }\r
6118   return FALSE;\r
6119 }\r
6120 \r
6121 /*---------------------------------------------------------------------------*\\r
6122  *\r
6123  * About box dialog functions\r
6124  *\r
6125 \*---------------------------------------------------------------------------*/\r
6126 \r
6127 /* Process messages for "About" dialog box */\r
6128 LRESULT CALLBACK\r
6129 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6130 {\r
6131   switch (message) {\r
6132   case WM_INITDIALOG: /* message: initialize dialog box */\r
6133     /* Center the dialog over the application window */\r
6134     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6135     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6136     Translate(hDlg, ABOUTBOX);\r
6137     JAWS_COPYRIGHT\r
6138     return (TRUE);\r
6139 \r
6140   case WM_COMMAND: /* message: received a command */\r
6141     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6142         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6143       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6144       return (TRUE);\r
6145     }\r
6146     break;\r
6147   }\r
6148   return (FALSE);\r
6149 }\r
6150 \r
6151 /*---------------------------------------------------------------------------*\\r
6152  *\r
6153  * Comment Dialog functions\r
6154  *\r
6155 \*---------------------------------------------------------------------------*/\r
6156 \r
6157 LRESULT CALLBACK\r
6158 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6159 {\r
6160   static HANDLE hwndText = NULL;\r
6161   int len, newSizeX, newSizeY, flags;\r
6162   static int sizeX, sizeY;\r
6163   char *str;\r
6164   RECT rect;\r
6165   MINMAXINFO *mmi;\r
6166 \r
6167   switch (message) {\r
6168   case WM_INITDIALOG: /* message: initialize dialog box */\r
6169     /* Initialize the dialog items */\r
6170     Translate(hDlg, DLG_EditComment);\r
6171     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6172     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6173     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6174     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6175     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6176     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6177     SetWindowText(hDlg, commentTitle);\r
6178     if (editComment) {\r
6179       SetFocus(hwndText);\r
6180     } else {\r
6181       SetFocus(GetDlgItem(hDlg, IDOK));\r
6182     }\r
6183     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6184                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6185                 MAKELPARAM(FALSE, 0));\r
6186     /* Size and position the dialog */\r
6187     if (!commentDialog) {\r
6188       commentDialog = hDlg;\r
6189       flags = SWP_NOZORDER;\r
6190       GetClientRect(hDlg, &rect);\r
6191       sizeX = rect.right;\r
6192       sizeY = rect.bottom;\r
6193       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6194           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6195         WINDOWPLACEMENT wp;\r
6196         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6197         wp.length = sizeof(WINDOWPLACEMENT);\r
6198         wp.flags = 0;\r
6199         wp.showCmd = SW_SHOW;\r
6200         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6201         wp.rcNormalPosition.left = wpComment.x;\r
6202         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6203         wp.rcNormalPosition.top = wpComment.y;\r
6204         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6205         SetWindowPlacement(hDlg, &wp);\r
6206 \r
6207         GetClientRect(hDlg, &rect);\r
6208         newSizeX = rect.right;\r
6209         newSizeY = rect.bottom;\r
6210         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6211                               newSizeX, newSizeY);\r
6212         sizeX = newSizeX;\r
6213         sizeY = newSizeY;\r
6214       }\r
6215     }\r
6216     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6217     return FALSE;\r
6218 \r
6219   case WM_COMMAND: /* message: received a command */\r
6220     switch (LOWORD(wParam)) {\r
6221     case IDOK:\r
6222       if (editComment) {\r
6223         char *p, *q;\r
6224         /* Read changed options from the dialog box */\r
6225         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6226         len = GetWindowTextLength(hwndText);\r
6227         str = (char *) malloc(len + 1);\r
6228         GetWindowText(hwndText, str, len + 1);\r
6229         p = q = str;\r
6230         while (*q) {\r
6231           if (*q == '\r')\r
6232             q++;\r
6233           else\r
6234             *p++ = *q++;\r
6235         }\r
6236         *p = NULLCHAR;\r
6237         ReplaceComment(commentIndex, str);\r
6238         free(str);\r
6239       }\r
6240       CommentPopDown();\r
6241       return TRUE;\r
6242 \r
6243     case IDCANCEL:\r
6244     case OPT_CancelComment:\r
6245       CommentPopDown();\r
6246       return TRUE;\r
6247 \r
6248     case OPT_ClearComment:\r
6249       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6250       break;\r
6251 \r
6252     case OPT_EditComment:\r
6253       EditCommentEvent();\r
6254       return TRUE;\r
6255 \r
6256     default:\r
6257       break;\r
6258     }\r
6259     break;\r
6260 \r
6261   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6262         if( wParam == OPT_CommentText ) {\r
6263             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6264 \r
6265             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6266                 POINTL pt;\r
6267                 LRESULT index;\r
6268 \r
6269                 pt.x = LOWORD( lpMF->lParam );\r
6270                 pt.y = HIWORD( lpMF->lParam );\r
6271 \r
6272                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6273 \r
6274                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6275                 len = GetWindowTextLength(hwndText);\r
6276                 str = (char *) malloc(len + 1);\r
6277                 GetWindowText(hwndText, str, len + 1);\r
6278                 ReplaceComment(commentIndex, str);\r
6279                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6280                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6281                 free(str);\r
6282 \r
6283                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6284                 lpMF->msg = WM_USER;\r
6285 \r
6286                 return TRUE;\r
6287             }\r
6288         }\r
6289         break;\r
6290 \r
6291   case WM_SIZE:\r
6292     newSizeX = LOWORD(lParam);\r
6293     newSizeY = HIWORD(lParam);\r
6294     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6295     sizeX = newSizeX;\r
6296     sizeY = newSizeY;\r
6297     break;\r
6298 \r
6299   case WM_GETMINMAXINFO:\r
6300     /* Prevent resizing window too small */\r
6301     mmi = (MINMAXINFO *) lParam;\r
6302     mmi->ptMinTrackSize.x = 100;\r
6303     mmi->ptMinTrackSize.y = 100;\r
6304     break;\r
6305   }\r
6306   return FALSE;\r
6307 }\r
6308 \r
6309 VOID\r
6310 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6311 {\r
6312   FARPROC lpProc;\r
6313   char *p, *q;\r
6314 \r
6315   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6316 \r
6317   if (str == NULL) str = "";\r
6318   p = (char *) malloc(2 * strlen(str) + 2);\r
6319   q = p;\r
6320   while (*str) {\r
6321     if (*str == '\n') *q++ = '\r';\r
6322     *q++ = *str++;\r
6323   }\r
6324   *q = NULLCHAR;\r
6325   if (commentText != NULL) free(commentText);\r
6326 \r
6327   commentIndex = index;\r
6328   commentTitle = title;\r
6329   commentText = p;\r
6330   editComment = edit;\r
6331 \r
6332   if (commentDialog) {\r
6333     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6334     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6335   } else {\r
6336     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6337     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6338                  hwndMain, (DLGPROC)lpProc);\r
6339     FreeProcInstance(lpProc);\r
6340   }\r
6341   commentUp = TRUE;\r
6342 }\r
6343 \r
6344 \r
6345 /*---------------------------------------------------------------------------*\\r
6346  *\r
6347  * Type-in move dialog functions\r
6348  * \r
6349 \*---------------------------------------------------------------------------*/\r
6350 \r
6351 LRESULT CALLBACK\r
6352 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6353 {\r
6354   char move[MSG_SIZ];\r
6355   HWND hInput;\r
6356   ChessMove moveType;\r
6357   int fromX, fromY, toX, toY;\r
6358   char promoChar;\r
6359 \r
6360   switch (message) {\r
6361   case WM_INITDIALOG:\r
6362     move[0] = (char) lParam;\r
6363     move[1] = NULLCHAR;\r
6364     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6365     Translate(hDlg, DLG_TypeInMove);\r
6366     hInput = GetDlgItem(hDlg, OPT_Move);\r
6367     SetWindowText(hInput, move);\r
6368     SetFocus(hInput);\r
6369     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6370     return FALSE;\r
6371 \r
6372   case WM_COMMAND:\r
6373     switch (LOWORD(wParam)) {\r
6374     case IDOK:
6375       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6376       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6377       { int n; Board board;\r
6378         // [HGM] FENedit\r
6379         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6380                 EditPositionPasteFEN(move);\r
6381                 EndDialog(hDlg, TRUE);\r
6382                 return TRUE;\r
6383         }\r
6384         // [HGM] movenum: allow move number to be typed in any mode\r
6385         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6386           ToNrEvent(2*n-1);\r
6387           EndDialog(hDlg, TRUE);\r
6388           return TRUE;\r
6389         }\r
6390       }\r
6391       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6392         gameMode != Training) {\r
6393         DisplayMoveError(_("Displayed move is not current"));\r
6394       } else {\r
6395 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6396         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6397           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6398         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6399         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6400           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6401           if (gameMode != Training)\r
6402               forwardMostMove = currentMove;\r
6403           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6404         } else {\r
6405           DisplayMoveError(_("Could not parse move"));\r
6406         }\r
6407       }\r
6408       EndDialog(hDlg, TRUE);\r
6409       return TRUE;\r
6410     case IDCANCEL:\r
6411       EndDialog(hDlg, FALSE);\r
6412       return TRUE;\r
6413     default:\r
6414       break;\r
6415     }\r
6416     break;\r
6417   }\r
6418   return FALSE;\r
6419 }\r
6420 \r
6421 VOID\r
6422 PopUpMoveDialog(char firstchar)\r
6423 {\r
6424     FARPROC lpProc;\r
6425     \r
6426     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6427         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6428         gameMode == AnalyzeMode || gameMode == EditGame || \r
6429         gameMode == EditPosition || gameMode == IcsExamining ||\r
6430         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6431         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6432                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6433                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6434         gameMode == Training) {\r
6435       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6436       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6437         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6438       FreeProcInstance(lpProc);\r
6439     }\r
6440 }\r
6441 \r
6442 /*---------------------------------------------------------------------------*\\r
6443  *\r
6444  * Type-in name dialog functions\r
6445  * \r
6446 \*---------------------------------------------------------------------------*/\r
6447 \r
6448 LRESULT CALLBACK\r
6449 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6450 {\r
6451   char move[MSG_SIZ];\r
6452   HWND hInput;\r
6453 \r
6454   switch (message) {\r
6455   case WM_INITDIALOG:\r
6456     move[0] = (char) lParam;\r
6457     move[1] = NULLCHAR;\r
6458     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6459     Translate(hDlg, DLG_TypeInName);\r
6460     hInput = GetDlgItem(hDlg, OPT_Name);\r
6461     SetWindowText(hInput, move);\r
6462     SetFocus(hInput);\r
6463     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6464     return FALSE;\r
6465 \r
6466   case WM_COMMAND:\r
6467     switch (LOWORD(wParam)) {\r
6468     case IDOK:\r
6469       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6470       appData.userName = strdup(move);\r
6471       SetUserLogo();\r
6472       SetGameInfo();\r
6473       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6474         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6475         DisplayTitle(move);\r
6476       }\r
6477 \r
6478 \r
6479       EndDialog(hDlg, TRUE);\r
6480       return TRUE;\r
6481     case IDCANCEL:\r
6482       EndDialog(hDlg, FALSE);\r
6483       return TRUE;\r
6484     default:\r
6485       break;\r
6486     }\r
6487     break;\r
6488   }\r
6489   return FALSE;\r
6490 }\r
6491 \r
6492 VOID\r
6493 PopUpNameDialog(char firstchar)\r
6494 {\r
6495     FARPROC lpProc;\r
6496     \r
6497       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6498       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6499         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6500       FreeProcInstance(lpProc);\r
6501 }\r
6502 \r
6503 /*---------------------------------------------------------------------------*\\r
6504  *\r
6505  *  Error dialogs\r
6506  * \r
6507 \*---------------------------------------------------------------------------*/\r
6508 \r
6509 /* Nonmodal error box */\r
6510 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6511                              WPARAM wParam, LPARAM lParam);\r
6512 \r
6513 VOID\r
6514 ErrorPopUp(char *title, char *content)\r
6515 {\r
6516   FARPROC lpProc;\r
6517   char *p, *q;\r
6518   BOOLEAN modal = hwndMain == NULL;\r
6519 \r
6520   p = content;\r
6521   q = errorMessage;\r
6522   while (*p) {\r
6523     if (*p == '\n') {\r
6524       if (modal) {\r
6525         *q++ = ' ';\r
6526         p++;\r
6527       } else {\r
6528         *q++ = '\r';\r
6529         *q++ = *p++;\r
6530       }\r
6531     } else {\r
6532       *q++ = *p++;\r
6533     }\r
6534   }\r
6535   *q = NULLCHAR;\r
6536   strncpy(errorTitle, title, sizeof(errorTitle));\r
6537   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6538   \r
6539   if (modal) {\r
6540     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6541   } else {\r
6542     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6543     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6544                  hwndMain, (DLGPROC)lpProc);\r
6545     FreeProcInstance(lpProc);\r
6546   }\r
6547 }\r
6548 \r
6549 VOID\r
6550 ErrorPopDown()\r
6551 {\r
6552   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6553   if (errorDialog == NULL) return;\r
6554   DestroyWindow(errorDialog);\r
6555   errorDialog = NULL;\r
6556   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6557 }\r
6558 \r
6559 LRESULT CALLBACK\r
6560 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6561 {\r
6562   HANDLE hwndText;\r
6563   RECT rChild;\r
6564 \r
6565   switch (message) {\r
6566   case WM_INITDIALOG:\r
6567     GetWindowRect(hDlg, &rChild);\r
6568 \r
6569     /*\r
6570     SetWindowPos(hDlg, NULL, rChild.left,\r
6571       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6572       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6573     */\r
6574 \r
6575     /* \r
6576         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6577         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6578         and it doesn't work when you resize the dialog.\r
6579         For now, just give it a default position.\r
6580     */\r
6581     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6582     Translate(hDlg, DLG_Error);\r
6583 \r
6584     errorDialog = hDlg;\r
6585     SetWindowText(hDlg, errorTitle);\r
6586     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6587     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6588     return FALSE;\r
6589 \r
6590   case WM_COMMAND:\r
6591     switch (LOWORD(wParam)) {\r
6592     case IDOK:\r
6593     case IDCANCEL:\r
6594       if (errorDialog == hDlg) errorDialog = NULL;\r
6595       DestroyWindow(hDlg);\r
6596       return TRUE;\r
6597 \r
6598     default:\r
6599       break;\r
6600     }\r
6601     break;\r
6602   }\r
6603   return FALSE;\r
6604 }\r
6605 \r
6606 #ifdef GOTHIC\r
6607 HWND gothicDialog = NULL;\r
6608 \r
6609 LRESULT CALLBACK\r
6610 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6611 {\r
6612   HANDLE hwndText;\r
6613   RECT rChild;\r
6614   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6615 \r
6616   switch (message) {\r
6617   case WM_INITDIALOG:\r
6618     GetWindowRect(hDlg, &rChild);\r
6619 \r
6620     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6621                                                              SWP_NOZORDER);\r
6622 \r
6623     /* \r
6624         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6625         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6626         and it doesn't work when you resize the dialog.\r
6627         For now, just give it a default position.\r
6628     */\r
6629     gothicDialog = hDlg;\r
6630     SetWindowText(hDlg, errorTitle);\r
6631     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6632     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6633     return FALSE;\r
6634 \r
6635   case WM_COMMAND:\r
6636     switch (LOWORD(wParam)) {\r
6637     case IDOK:\r
6638     case IDCANCEL:\r
6639       if (errorDialog == hDlg) errorDialog = NULL;\r
6640       DestroyWindow(hDlg);\r
6641       return TRUE;\r
6642 \r
6643     default:\r
6644       break;\r
6645     }\r
6646     break;\r
6647   }\r
6648   return FALSE;\r
6649 }\r
6650 \r
6651 VOID\r
6652 GothicPopUp(char *title, VariantClass variant)\r
6653 {\r
6654   FARPROC lpProc;\r
6655   static char *lastTitle;\r
6656 \r
6657   strncpy(errorTitle, title, sizeof(errorTitle));\r
6658   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6659 \r
6660   if(lastTitle != title && gothicDialog != NULL) {\r
6661     DestroyWindow(gothicDialog);\r
6662     gothicDialog = NULL;\r
6663   }\r
6664   if(variant != VariantNormal && gothicDialog == NULL) {\r
6665     title = lastTitle;\r
6666     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6667     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6668                  hwndMain, (DLGPROC)lpProc);\r
6669     FreeProcInstance(lpProc);\r
6670   }\r
6671 }\r
6672 #endif\r
6673 \r
6674 /*---------------------------------------------------------------------------*\\r
6675  *\r
6676  *  Ics Interaction console functions\r
6677  *\r
6678 \*---------------------------------------------------------------------------*/\r
6679 \r
6680 #define HISTORY_SIZE 64\r
6681 static char *history[HISTORY_SIZE];\r
6682 int histIn = 0, histP = 0;\r
6683 \r
6684 VOID\r
6685 SaveInHistory(char *cmd)\r
6686 {\r
6687   if (history[histIn] != NULL) {\r
6688     free(history[histIn]);\r
6689     history[histIn] = NULL;\r
6690   }\r
6691   if (*cmd == NULLCHAR) return;\r
6692   history[histIn] = StrSave(cmd);\r
6693   histIn = (histIn + 1) % HISTORY_SIZE;\r
6694   if (history[histIn] != NULL) {\r
6695     free(history[histIn]);\r
6696     history[histIn] = NULL;\r
6697   }\r
6698   histP = histIn;\r
6699 }\r
6700 \r
6701 char *\r
6702 PrevInHistory(char *cmd)\r
6703 {\r
6704   int newhp;\r
6705   if (histP == histIn) {\r
6706     if (history[histIn] != NULL) free(history[histIn]);\r
6707     history[histIn] = StrSave(cmd);\r
6708   }\r
6709   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6710   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6711   histP = newhp;\r
6712   return history[histP];\r
6713 }\r
6714 \r
6715 char *\r
6716 NextInHistory()\r
6717 {\r
6718   if (histP == histIn) return NULL;\r
6719   histP = (histP + 1) % HISTORY_SIZE;\r
6720   return history[histP];   \r
6721 }\r
6722 \r
6723 HMENU\r
6724 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6725 {\r
6726   HMENU hmenu, h;\r
6727   int i = 0;\r
6728   hmenu = LoadMenu(hInst, "TextMenu");\r
6729   h = GetSubMenu(hmenu, 0);\r
6730   while (e->item) {\r
6731     if (strcmp(e->item, "-") == 0) {\r
6732       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6733     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6734       int flags = MF_STRING, j = 0;\r
6735       if (e->item[0] == '|') {\r
6736         flags |= MF_MENUBARBREAK;\r
6737         j++;\r
6738       }\r
6739       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6740       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6741     }\r
6742     e++;\r
6743     i++;\r
6744   } \r
6745   return hmenu;\r
6746 }\r
6747 \r
6748 WNDPROC consoleTextWindowProc;\r
6749 \r
6750 void\r
6751 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6752 {\r
6753   char buf[MSG_SIZ], name[MSG_SIZ];\r
6754   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6755   CHARRANGE sel;\r
6756 \r
6757   if (!getname) {\r
6758     SetWindowText(hInput, command);\r
6759     if (immediate) {\r
6760       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6761     } else {\r
6762       sel.cpMin = 999999;\r
6763       sel.cpMax = 999999;\r
6764       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6765       SetFocus(hInput);\r
6766     }\r
6767     return;\r
6768   }    \r
6769   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6770   if (sel.cpMin == sel.cpMax) {\r
6771     /* Expand to surrounding word */\r
6772     TEXTRANGE tr;\r
6773     do {\r
6774       tr.chrg.cpMax = sel.cpMin;\r
6775       tr.chrg.cpMin = --sel.cpMin;\r
6776       if (sel.cpMin < 0) break;\r
6777       tr.lpstrText = name;\r
6778       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6779     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6780     sel.cpMin++;\r
6781 \r
6782     do {\r
6783       tr.chrg.cpMin = sel.cpMax;\r
6784       tr.chrg.cpMax = ++sel.cpMax;\r
6785       tr.lpstrText = name;\r
6786       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6787     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6788     sel.cpMax--;\r
6789 \r
6790     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6791       MessageBeep(MB_ICONEXCLAMATION);\r
6792       return;\r
6793     }\r
6794     tr.chrg = sel;\r
6795     tr.lpstrText = name;\r
6796     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6797   } else {\r
6798     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6799       MessageBeep(MB_ICONEXCLAMATION);\r
6800       return;\r
6801     }\r
6802     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6803   }\r
6804   if (immediate) {\r
6805     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6806     SetWindowText(hInput, buf);\r
6807     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6808   } else {\r
6809     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6810       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6811     SetWindowText(hInput, buf);\r
6812     sel.cpMin = 999999;\r
6813     sel.cpMax = 999999;\r
6814     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6815     SetFocus(hInput);\r
6816   }\r
6817 }\r
6818 \r
6819 LRESULT CALLBACK \r
6820 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6821 {\r
6822   HWND hInput;\r
6823   CHARRANGE sel;\r
6824 \r
6825   switch (message) {\r
6826   case WM_KEYDOWN:\r
6827     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6828     switch (wParam) {\r
6829     case VK_PRIOR:\r
6830       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6831       return 0;\r
6832     case VK_NEXT:\r
6833       sel.cpMin = 999999;\r
6834       sel.cpMax = 999999;\r
6835       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6836       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6837       return 0;\r
6838     }\r
6839     break;\r
6840   case WM_CHAR:\r
6841    if(wParam != '\022') {\r
6842     if (wParam == '\t') {\r
6843       if (GetKeyState(VK_SHIFT) < 0) {\r
6844         /* shifted */\r
6845         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6846         if (buttonDesc[0].hwnd) {\r
6847           SetFocus(buttonDesc[0].hwnd);\r
6848         } else {\r
6849           SetFocus(hwndMain);\r
6850         }\r
6851       } else {\r
6852         /* unshifted */\r
6853         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6854       }\r
6855     } else {\r
6856       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6857       JAWS_DELETE( SetFocus(hInput); )\r
6858       SendMessage(hInput, message, wParam, lParam);\r
6859     }\r
6860     return 0;\r
6861    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6862   case WM_RBUTTONDOWN:\r
6863     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6864       /* Move selection here if it was empty */\r
6865       POINT pt;\r
6866       pt.x = LOWORD(lParam);\r
6867       pt.y = HIWORD(lParam);\r
6868       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6869       if (sel.cpMin == sel.cpMax) {\r
6870         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6871         sel.cpMax = sel.cpMin;\r
6872         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6873       }\r
6874       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6875 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6876       POINT pt;\r
6877       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6878       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6879       if (sel.cpMin == sel.cpMax) {\r
6880         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6881         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6882       }\r
6883       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6884         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6885       }\r
6886       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6887       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6888       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6889       MenuPopup(hwnd, pt, hmenu, -1);\r
6890 }\r
6891     }\r
6892     return 0;\r
6893   case WM_RBUTTONUP:\r
6894     if (GetKeyState(VK_SHIFT) & ~1) {\r
6895       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6896         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6897     }\r
6898     return 0;\r
6899   case WM_PASTE:\r
6900     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6901     SetFocus(hInput);\r
6902     return SendMessage(hInput, message, wParam, lParam);\r
6903   case WM_MBUTTONDOWN:\r
6904     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6905   case WM_COMMAND:\r
6906     switch (LOWORD(wParam)) {\r
6907     case IDM_QuickPaste:\r
6908       {\r
6909         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6910         if (sel.cpMin == sel.cpMax) {\r
6911           MessageBeep(MB_ICONEXCLAMATION);\r
6912           return 0;\r
6913         }\r
6914         SendMessage(hwnd, WM_COPY, 0, 0);\r
6915         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6916         SendMessage(hInput, WM_PASTE, 0, 0);\r
6917         SetFocus(hInput);\r
6918         return 0;\r
6919       }\r
6920     case IDM_Cut:\r
6921       SendMessage(hwnd, WM_CUT, 0, 0);\r
6922       return 0;\r
6923     case IDM_Paste:\r
6924       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6925       return 0;\r
6926     case IDM_Copy:\r
6927       SendMessage(hwnd, WM_COPY, 0, 0);\r
6928       return 0;\r
6929     default:\r
6930       {\r
6931         int i = LOWORD(wParam) - IDM_CommandX;\r
6932         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6933             icsTextMenuEntry[i].command != NULL) {\r
6934           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6935                    icsTextMenuEntry[i].getname,\r
6936                    icsTextMenuEntry[i].immediate);\r
6937           return 0;\r
6938         }\r
6939       }\r
6940       break;\r
6941     }\r
6942     break;\r
6943   }\r
6944   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6945 }\r
6946 \r
6947 WNDPROC consoleInputWindowProc;\r
6948 \r
6949 LRESULT CALLBACK\r
6950 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6951 {\r
6952   char buf[MSG_SIZ];\r
6953   char *p;\r
6954   static BOOL sendNextChar = FALSE;\r
6955   static BOOL quoteNextChar = FALSE;\r
6956   InputSource *is = consoleInputSource;\r
6957   CHARFORMAT cf;\r
6958   CHARRANGE sel;\r
6959 \r
6960   switch (message) {\r
6961   case WM_CHAR:\r
6962     if (!appData.localLineEditing || sendNextChar) {\r
6963       is->buf[0] = (CHAR) wParam;\r
6964       is->count = 1;\r
6965       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6966       sendNextChar = FALSE;\r
6967       return 0;\r
6968     }\r
6969     if (quoteNextChar) {\r
6970       buf[0] = (char) wParam;\r
6971       buf[1] = NULLCHAR;\r
6972       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6973       quoteNextChar = FALSE;\r
6974       return 0;\r
6975     }\r
6976     switch (wParam) {\r
6977     case '\r':   /* Enter key */\r
6978       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6979       if (consoleEcho) SaveInHistory(is->buf);\r
6980       is->buf[is->count++] = '\n';\r
6981       is->buf[is->count] = NULLCHAR;\r
6982       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6983       if (consoleEcho) {\r
6984         ConsoleOutput(is->buf, is->count, TRUE);\r
6985       } else if (appData.localLineEditing) {\r
6986         ConsoleOutput("\n", 1, TRUE);\r
6987       }\r
6988       /* fall thru */\r
6989     case '\033': /* Escape key */\r
6990       SetWindowText(hwnd, "");\r
6991       cf.cbSize = sizeof(CHARFORMAT);\r
6992       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6993       if (consoleEcho) {\r
6994         cf.crTextColor = textAttribs[ColorNormal].color;\r
6995       } else {\r
6996         cf.crTextColor = COLOR_ECHOOFF;\r
6997       }\r
6998       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6999       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7000       return 0;\r
7001     case '\t':   /* Tab key */\r
7002       if (GetKeyState(VK_SHIFT) < 0) {\r
7003         /* shifted */\r
7004         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7005       } else {\r
7006         /* unshifted */\r
7007         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7008         if (buttonDesc[0].hwnd) {\r
7009           SetFocus(buttonDesc[0].hwnd);\r
7010         } else {\r
7011           SetFocus(hwndMain);\r
7012         }\r
7013       }\r
7014       return 0;\r
7015     case '\023': /* Ctrl+S */\r
7016       sendNextChar = TRUE;\r
7017       return 0;\r
7018     case '\021': /* Ctrl+Q */\r
7019       quoteNextChar = TRUE;\r
7020       return 0;\r
7021     JAWS_REPLAY\r
7022     default:\r
7023       break;\r
7024     }\r
7025     break;\r
7026   case WM_KEYDOWN:\r
7027     switch (wParam) {\r
7028     case VK_UP:\r
7029       GetWindowText(hwnd, buf, MSG_SIZ);\r
7030       p = PrevInHistory(buf);\r
7031       if (p != NULL) {\r
7032         SetWindowText(hwnd, p);\r
7033         sel.cpMin = 999999;\r
7034         sel.cpMax = 999999;\r
7035         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7036         return 0;\r
7037       }\r
7038       break;\r
7039     case VK_DOWN:\r
7040       p = NextInHistory();\r
7041       if (p != NULL) {\r
7042         SetWindowText(hwnd, p);\r
7043         sel.cpMin = 999999;\r
7044         sel.cpMax = 999999;\r
7045         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7046         return 0;\r
7047       }\r
7048       break;\r
7049     case VK_HOME:\r
7050     case VK_END:\r
7051       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7052       /* fall thru */\r
7053     case VK_PRIOR:\r
7054     case VK_NEXT:\r
7055       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7056       return 0;\r
7057     }\r
7058     break;\r
7059   case WM_MBUTTONDOWN:\r
7060     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7061       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7062     break;\r
7063   case WM_RBUTTONUP:\r
7064     if (GetKeyState(VK_SHIFT) & ~1) {\r
7065       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7066         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7067     } else {\r
7068       POINT pt;\r
7069       HMENU hmenu;\r
7070       hmenu = LoadMenu(hInst, "InputMenu");\r
7071       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7072       if (sel.cpMin == sel.cpMax) {\r
7073         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7074         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7075       }\r
7076       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7077         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7078       }\r
7079       pt.x = LOWORD(lParam);\r
7080       pt.y = HIWORD(lParam);\r
7081       MenuPopup(hwnd, pt, hmenu, -1);\r
7082     }\r
7083     return 0;\r
7084   case WM_COMMAND:\r
7085     switch (LOWORD(wParam)) { \r
7086     case IDM_Undo:\r
7087       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7088       return 0;\r
7089     case IDM_SelectAll:\r
7090       sel.cpMin = 0;\r
7091       sel.cpMax = -1; /*999999?*/\r
7092       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7093       return 0;\r
7094     case IDM_Cut:\r
7095       SendMessage(hwnd, WM_CUT, 0, 0);\r
7096       return 0;\r
7097     case IDM_Paste:\r
7098       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7099       return 0;\r
7100     case IDM_Copy:\r
7101       SendMessage(hwnd, WM_COPY, 0, 0);\r
7102       return 0;\r
7103     }\r
7104     break;\r
7105   }\r
7106   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7107 }\r
7108 \r
7109 #define CO_MAX  100000\r
7110 #define CO_TRIM   1000\r
7111 \r
7112 LRESULT CALLBACK\r
7113 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7114 {\r
7115   static SnapData sd;\r
7116   HWND hText, hInput;\r
7117   RECT rect;\r
7118   static int sizeX, sizeY;\r
7119   int newSizeX, newSizeY;\r
7120   MINMAXINFO *mmi;\r
7121   WORD wMask;\r
7122 \r
7123   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7124   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7125 \r
7126   switch (message) {\r
7127   case WM_NOTIFY:\r
7128     if (((NMHDR*)lParam)->code == EN_LINK)\r
7129     {\r
7130       ENLINK *pLink = (ENLINK*)lParam;\r
7131       if (pLink->msg == WM_LBUTTONUP)\r
7132       {\r
7133         TEXTRANGE tr;\r
7134 \r
7135         tr.chrg = pLink->chrg;\r
7136         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7137         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7138         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7139         free(tr.lpstrText);\r
7140       }\r
7141     }\r
7142     break;\r
7143   case WM_INITDIALOG: /* message: initialize dialog box */\r
7144     hwndConsole = hDlg;\r
7145     SetFocus(hInput);\r
7146     consoleTextWindowProc = (WNDPROC)\r
7147       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7148     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7149     consoleInputWindowProc = (WNDPROC)\r
7150       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7151     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7152     Colorize(ColorNormal, TRUE);\r
7153     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7154     ChangedConsoleFont();\r
7155     GetClientRect(hDlg, &rect);\r
7156     sizeX = rect.right;\r
7157     sizeY = rect.bottom;\r
7158     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7159         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7160       WINDOWPLACEMENT wp;\r
7161       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7162       wp.length = sizeof(WINDOWPLACEMENT);\r
7163       wp.flags = 0;\r
7164       wp.showCmd = SW_SHOW;\r
7165       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7166       wp.rcNormalPosition.left = wpConsole.x;\r
7167       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7168       wp.rcNormalPosition.top = wpConsole.y;\r
7169       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7170       SetWindowPlacement(hDlg, &wp);\r
7171     }\r
7172 \r
7173    // [HGM] Chessknight's change 2004-07-13\r
7174    else { /* Determine Defaults */\r
7175        WINDOWPLACEMENT wp;\r
7176        wpConsole.x = wpMain.width + 1;\r
7177        wpConsole.y = wpMain.y;\r
7178        wpConsole.width = screenWidth -  wpMain.width;\r
7179        wpConsole.height = wpMain.height;\r
7180        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7181        wp.length = sizeof(WINDOWPLACEMENT);\r
7182        wp.flags = 0;\r
7183        wp.showCmd = SW_SHOW;\r
7184        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7185        wp.rcNormalPosition.left = wpConsole.x;\r
7186        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7187        wp.rcNormalPosition.top = wpConsole.y;\r
7188        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7189        SetWindowPlacement(hDlg, &wp);\r
7190     }\r
7191 \r
7192    // Allow hText to highlight URLs and send notifications on them\r
7193    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7194    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7195    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7196    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7197 \r
7198     return FALSE;\r
7199 \r
7200   case WM_SETFOCUS:\r
7201     SetFocus(hInput);\r
7202     return 0;\r
7203 \r
7204   case WM_CLOSE:\r
7205     ExitEvent(0);\r
7206     /* not reached */\r
7207     break;\r
7208 \r
7209   case WM_SIZE:\r
7210     if (IsIconic(hDlg)) break;\r
7211     newSizeX = LOWORD(lParam);\r
7212     newSizeY = HIWORD(lParam);\r
7213     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7214       RECT rectText, rectInput;\r
7215       POINT pt;\r
7216       int newTextHeight, newTextWidth;\r
7217       GetWindowRect(hText, &rectText);\r
7218       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7219       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7220       if (newTextHeight < 0) {\r
7221         newSizeY += -newTextHeight;\r
7222         newTextHeight = 0;\r
7223       }\r
7224       SetWindowPos(hText, NULL, 0, 0,\r
7225         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7226       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7227       pt.x = rectInput.left;\r
7228       pt.y = rectInput.top + newSizeY - sizeY;\r
7229       ScreenToClient(hDlg, &pt);\r
7230       SetWindowPos(hInput, NULL, \r
7231         pt.x, pt.y, /* needs client coords */   \r
7232         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7233         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7234     }\r
7235     sizeX = newSizeX;\r
7236     sizeY = newSizeY;\r
7237     break;\r
7238 \r
7239   case WM_GETMINMAXINFO:\r
7240     /* Prevent resizing window too small */\r
7241     mmi = (MINMAXINFO *) lParam;\r
7242     mmi->ptMinTrackSize.x = 100;\r
7243     mmi->ptMinTrackSize.y = 100;\r
7244     break;\r
7245 \r
7246   /* [AS] Snapping */\r
7247   case WM_ENTERSIZEMOVE:\r
7248     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7249 \r
7250   case WM_SIZING:\r
7251     return OnSizing( &sd, hDlg, wParam, lParam );\r
7252 \r
7253   case WM_MOVING:\r
7254     return OnMoving( &sd, hDlg, wParam, lParam );\r
7255 \r
7256   case WM_EXITSIZEMOVE:\r
7257         UpdateICSWidth(hText);\r
7258     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7259   }\r
7260 \r
7261   return DefWindowProc(hDlg, message, wParam, lParam);\r
7262 }\r
7263 \r
7264 \r
7265 VOID\r
7266 ConsoleCreate()\r
7267 {\r
7268   HWND hCons;\r
7269   if (hwndConsole) return;\r
7270   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7271   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7272 }\r
7273 \r
7274 \r
7275 VOID\r
7276 ConsoleOutput(char* data, int length, int forceVisible)\r
7277 {\r
7278   HWND hText;\r
7279   int trim, exlen;\r
7280   char *p, *q;\r
7281   char buf[CO_MAX+1];\r
7282   POINT pEnd;\r
7283   RECT rect;\r
7284   static int delayLF = 0;\r
7285   CHARRANGE savesel, sel;\r
7286 \r
7287   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7288   p = data;\r
7289   q = buf;\r
7290   if (delayLF) {\r
7291     *q++ = '\r';\r
7292     *q++ = '\n';\r
7293     delayLF = 0;\r
7294   }\r
7295   while (length--) {\r
7296     if (*p == '\n') {\r
7297       if (*++p) {\r
7298         *q++ = '\r';\r
7299         *q++ = '\n';\r
7300       } else {\r
7301         delayLF = 1;\r
7302       }\r
7303     } else if (*p == '\007') {\r
7304        MyPlaySound(&sounds[(int)SoundBell]);\r
7305        p++;\r
7306     } else {\r
7307       *q++ = *p++;\r
7308     }\r
7309   }\r
7310   *q = NULLCHAR;\r
7311   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7312   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7313   /* Save current selection */\r
7314   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7315   exlen = GetWindowTextLength(hText);\r
7316   /* Find out whether current end of text is visible */\r
7317   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7318   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7319   /* Trim existing text if it's too long */\r
7320   if (exlen + (q - buf) > CO_MAX) {\r
7321     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7322     sel.cpMin = 0;\r
7323     sel.cpMax = trim;\r
7324     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7325     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7326     exlen -= trim;\r
7327     savesel.cpMin -= trim;\r
7328     savesel.cpMax -= trim;\r
7329     if (exlen < 0) exlen = 0;\r
7330     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7331     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7332   }\r
7333   /* Append the new text */\r
7334   sel.cpMin = exlen;\r
7335   sel.cpMax = exlen;\r
7336   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7337   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7338   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7339   if (forceVisible || exlen == 0 ||\r
7340       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7341        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7342     /* Scroll to make new end of text visible if old end of text\r
7343        was visible or new text is an echo of user typein */\r
7344     sel.cpMin = 9999999;\r
7345     sel.cpMax = 9999999;\r
7346     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7347     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7348     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7349     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7350   }\r
7351   if (savesel.cpMax == exlen || forceVisible) {\r
7352     /* Move insert point to new end of text if it was at the old\r
7353        end of text or if the new text is an echo of user typein */\r
7354     sel.cpMin = 9999999;\r
7355     sel.cpMax = 9999999;\r
7356     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7357   } else {\r
7358     /* Restore previous selection */\r
7359     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7360   }\r
7361   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7362 }\r
7363 \r
7364 /*---------*/\r
7365 \r
7366 \r
7367 void\r
7368 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7369 {\r
7370   char buf[100];\r
7371   char *str;\r
7372   COLORREF oldFg, oldBg;\r
7373   HFONT oldFont;\r
7374   RECT rect;\r
7375 \r
7376   if(copyNumber > 1)
7377     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7378 \r
7379   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7380   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7381   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7382 \r
7383   rect.left = x;\r
7384   rect.right = x + squareSize;\r
7385   rect.top  = y;\r
7386   rect.bottom = y + squareSize;\r
7387   str = buf;\r
7388 \r
7389   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7390                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7391              y, ETO_CLIPPED|ETO_OPAQUE,\r
7392              &rect, str, strlen(str), NULL);\r
7393 \r
7394   (void) SetTextColor(hdc, oldFg);\r
7395   (void) SetBkColor(hdc, oldBg);\r
7396   (void) SelectObject(hdc, oldFont);\r
7397 }\r
7398 \r
7399 void\r
7400 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7401               RECT *rect, char *color, char *flagFell)\r
7402 {\r
7403   char buf[100];\r
7404   char *str;\r
7405   COLORREF oldFg, oldBg;\r
7406   HFONT oldFont;\r
7407 \r
7408   if (appData.clockMode) {\r
7409     if (tinyLayout)\r
7410       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7411     else\r
7412       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7413     str = buf;\r
7414   } else {\r
7415     str = color;\r
7416   }\r
7417 \r
7418   if (highlight) {\r
7419     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7420     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7421   } else {\r
7422     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7423     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7424   }\r
7425   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7426 \r
7427   JAWS_SILENCE\r
7428 \r
7429   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7430              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7431              rect, str, strlen(str), NULL);\r
7432   if(logoHeight > 0 && appData.clockMode) {\r
7433       RECT r;\r
7434       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);\r
7435       r.top = rect->top + logoHeight/2;\r
7436       r.left = rect->left;\r
7437       r.right = rect->right;\r
7438       r.bottom = rect->bottom;\r
7439       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7440                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7441                  &r, str, strlen(str), NULL);\r
7442   }\r
7443   (void) SetTextColor(hdc, oldFg);\r
7444   (void) SetBkColor(hdc, oldBg);\r
7445   (void) SelectObject(hdc, oldFont);\r
7446 }\r
7447 \r
7448 \r
7449 int\r
7450 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7451            OVERLAPPED *ovl)\r
7452 {\r
7453   int ok, err;\r
7454 \r
7455   /* [AS]  */\r
7456   if( count <= 0 ) {\r
7457     if (appData.debugMode) {\r
7458       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7459     }\r
7460 \r
7461     return ERROR_INVALID_USER_BUFFER;\r
7462   }\r
7463 \r
7464   ResetEvent(ovl->hEvent);\r
7465   ovl->Offset = ovl->OffsetHigh = 0;\r
7466   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7467   if (ok) {\r
7468     err = NO_ERROR;\r
7469   } else {\r
7470     err = GetLastError();\r
7471     if (err == ERROR_IO_PENDING) {\r
7472       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7473       if (ok)\r
7474         err = NO_ERROR;\r
7475       else\r
7476         err = GetLastError();\r
7477     }\r
7478   }\r
7479   return err;\r
7480 }\r
7481 \r
7482 int\r
7483 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7484             OVERLAPPED *ovl)\r
7485 {\r
7486   int ok, err;\r
7487 \r
7488   ResetEvent(ovl->hEvent);\r
7489   ovl->Offset = ovl->OffsetHigh = 0;\r
7490   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7491   if (ok) {\r
7492     err = NO_ERROR;\r
7493   } else {\r
7494     err = GetLastError();\r
7495     if (err == ERROR_IO_PENDING) {\r
7496       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7497       if (ok)\r
7498         err = NO_ERROR;\r
7499       else\r
7500         err = GetLastError();\r
7501     }\r
7502   }\r
7503   return err;\r
7504 }\r
7505 \r
7506 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7507 void CheckForInputBufferFull( InputSource * is )\r
7508 {\r
7509     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7510         /* Look for end of line */\r
7511         char * p = is->buf;\r
7512         \r
7513         while( p < is->next && *p != '\n' ) {\r
7514             p++;\r
7515         }\r
7516 \r
7517         if( p >= is->next ) {\r
7518             if (appData.debugMode) {\r
7519                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7520             }\r
7521 \r
7522             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7523             is->count = (DWORD) -1;\r
7524             is->next = is->buf;\r
7525         }\r
7526     }\r
7527 }\r
7528 \r
7529 DWORD\r
7530 InputThread(LPVOID arg)\r
7531 {\r
7532   InputSource *is;\r
7533   OVERLAPPED ovl;\r
7534 \r
7535   is = (InputSource *) arg;\r
7536   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7537   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7538   while (is->hThread != NULL) {\r
7539     is->error = DoReadFile(is->hFile, is->next,\r
7540                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7541                            &is->count, &ovl);\r
7542     if (is->error == NO_ERROR) {\r
7543       is->next += is->count;\r
7544     } else {\r
7545       if (is->error == ERROR_BROKEN_PIPE) {\r
7546         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7547         is->count = 0;\r
7548       } else {\r
7549         is->count = (DWORD) -1;\r
7550         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7551         break; \r
7552       }\r
7553     }\r
7554 \r
7555     CheckForInputBufferFull( is );\r
7556 \r
7557     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7558 \r
7559     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7560 \r
7561     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7562   }\r
7563 \r
7564   CloseHandle(ovl.hEvent);\r
7565   CloseHandle(is->hFile);\r
7566 \r
7567   if (appData.debugMode) {\r
7568     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7569   }\r
7570 \r
7571   return 0;\r
7572 }\r
7573 \r
7574 \r
7575 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7576 DWORD\r
7577 NonOvlInputThread(LPVOID arg)\r
7578 {\r
7579   InputSource *is;\r
7580   char *p, *q;\r
7581   int i;\r
7582   char prev;\r
7583 \r
7584   is = (InputSource *) arg;\r
7585   while (is->hThread != NULL) {\r
7586     is->error = ReadFile(is->hFile, is->next,\r
7587                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7588                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7589     if (is->error == NO_ERROR) {\r
7590       /* Change CRLF to LF */\r
7591       if (is->next > is->buf) {\r
7592         p = is->next - 1;\r
7593         i = is->count + 1;\r
7594       } else {\r
7595         p = is->next;\r
7596         i = is->count;\r
7597       }\r
7598       q = p;\r
7599       prev = NULLCHAR;\r
7600       while (i > 0) {\r
7601         if (prev == '\r' && *p == '\n') {\r
7602           *(q-1) = '\n';\r
7603           is->count--;\r
7604         } else { \r
7605           *q++ = *p;\r
7606         }\r
7607         prev = *p++;\r
7608         i--;\r
7609       }\r
7610       *q = NULLCHAR;\r
7611       is->next = q;\r
7612     } else {\r
7613       if (is->error == ERROR_BROKEN_PIPE) {\r
7614         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7615         is->count = 0; \r
7616       } else {\r
7617         is->count = (DWORD) -1;\r
7618       }\r
7619     }\r
7620 \r
7621     CheckForInputBufferFull( is );\r
7622 \r
7623     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7624 \r
7625     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7626 \r
7627     if (is->count < 0) break;  /* Quit on error */\r
7628   }\r
7629   CloseHandle(is->hFile);\r
7630   return 0;\r
7631 }\r
7632 \r
7633 DWORD\r
7634 SocketInputThread(LPVOID arg)\r
7635 {\r
7636   InputSource *is;\r
7637 \r
7638   is = (InputSource *) arg;\r
7639   while (is->hThread != NULL) {\r
7640     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7641     if ((int)is->count == SOCKET_ERROR) {\r
7642       is->count = (DWORD) -1;\r
7643       is->error = WSAGetLastError();\r
7644     } else {\r
7645       is->error = NO_ERROR;\r
7646       is->next += is->count;\r
7647       if (is->count == 0 && is->second == is) {\r
7648         /* End of file on stderr; quit with no message */\r
7649         break;\r
7650       }\r
7651     }\r
7652     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7653 \r
7654     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7655 \r
7656     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7657   }\r
7658   return 0;\r
7659 }\r
7660 \r
7661 VOID\r
7662 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7663 {\r
7664   InputSource *is;\r
7665 \r
7666   is = (InputSource *) lParam;\r
7667   if (is->lineByLine) {\r
7668     /* Feed in lines one by one */\r
7669     char *p = is->buf;\r
7670     char *q = p;\r
7671     while (q < is->next) {\r
7672       if (*q++ == '\n') {\r
7673         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7674         p = q;\r
7675       }\r
7676     }\r
7677     \r
7678     /* Move any partial line to the start of the buffer */\r
7679     q = is->buf;\r
7680     while (p < is->next) {\r
7681       *q++ = *p++;\r
7682     }\r
7683     is->next = q;\r
7684 \r
7685     if (is->error != NO_ERROR || is->count == 0) {\r
7686       /* Notify backend of the error.  Note: If there was a partial\r
7687          line at the end, it is not flushed through. */\r
7688       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7689     }\r
7690   } else {\r
7691     /* Feed in the whole chunk of input at once */\r
7692     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7693     is->next = is->buf;\r
7694   }\r
7695 }\r
7696 \r
7697 /*---------------------------------------------------------------------------*\\r
7698  *\r
7699  *  Menu enables. Used when setting various modes.\r
7700  *\r
7701 \*---------------------------------------------------------------------------*/\r
7702 \r
7703 typedef struct {\r
7704   int item;\r
7705   int flags;\r
7706 } Enables;\r
7707 \r
7708 VOID\r
7709 GreyRevert(Boolean grey)\r
7710 { // [HGM] vari: for retracting variations in local mode\r
7711   HMENU hmenu = GetMenu(hwndMain);\r
7712   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7713   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7714 }\r
7715 \r
7716 VOID\r
7717 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7718 {\r
7719   while (enab->item > 0) {\r
7720     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7721     enab++;\r
7722   }\r
7723 }\r
7724 \r
7725 Enables gnuEnables[] = {\r
7726   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7727   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7728   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7729   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7730   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7731   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7732   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7733   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7734   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7735   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7736   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7737   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7738   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7739   { -1, -1 }\r
7740 };\r
7741 \r
7742 Enables icsEnables[] = {\r
7743   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7747   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7751   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7753   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7754   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7755   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7756   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7757   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7760   { -1, -1 }\r
7761 };\r
7762 \r
7763 #if ZIPPY\r
7764 Enables zippyEnables[] = {\r
7765   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7766   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7767   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7768   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7769   { -1, -1 }\r
7770 };\r
7771 #endif\r
7772 \r
7773 Enables ncpEnables[] = {\r
7774   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7783   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7784   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7795   { -1, -1 }\r
7796 };\r
7797 \r
7798 Enables trainingOnEnables[] = {\r
7799   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7807   { -1, -1 }\r
7808 };\r
7809 \r
7810 Enables trainingOffEnables[] = {\r
7811   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7812   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7813   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7814   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7815   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7816   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7817   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7818   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7819   { -1, -1 }\r
7820 };\r
7821 \r
7822 /* These modify either ncpEnables or gnuEnables */\r
7823 Enables cmailEnables[] = {\r
7824   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7825   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7826   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7827   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7829   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7831   { -1, -1 }\r
7832 };\r
7833 \r
7834 Enables machineThinkingEnables[] = {\r
7835   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7851   { -1, -1 }\r
7852 };\r
7853 \r
7854 Enables userThinkingEnables[] = {\r
7855   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7868   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7870   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7871   { -1, -1 }\r
7872 };\r
7873 \r
7874 /*---------------------------------------------------------------------------*\\r
7875  *\r
7876  *  Front-end interface functions exported by XBoard.\r
7877  *  Functions appear in same order as prototypes in frontend.h.\r
7878  * \r
7879 \*---------------------------------------------------------------------------*/\r
7880 VOID\r
7881 ModeHighlight()\r
7882 {\r
7883   static UINT prevChecked = 0;\r
7884   static int prevPausing = 0;\r
7885   UINT nowChecked;\r
7886 \r
7887   if (pausing != prevPausing) {\r
7888     prevPausing = pausing;\r
7889     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7890                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7891     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7892   }\r
7893 \r
7894   switch (gameMode) {\r
7895   case BeginningOfGame:\r
7896     if (appData.icsActive)\r
7897       nowChecked = IDM_IcsClient;\r
7898     else if (appData.noChessProgram)\r
7899       nowChecked = IDM_EditGame;\r
7900     else\r
7901       nowChecked = IDM_MachineBlack;\r
7902     break;\r
7903   case MachinePlaysBlack:\r
7904     nowChecked = IDM_MachineBlack;\r
7905     break;\r
7906   case MachinePlaysWhite:\r
7907     nowChecked = IDM_MachineWhite;\r
7908     break;\r
7909   case TwoMachinesPlay:\r
7910     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7911     break;\r
7912   case AnalyzeMode:\r
7913     nowChecked = IDM_AnalysisMode;\r
7914     break;\r
7915   case AnalyzeFile:\r
7916     nowChecked = IDM_AnalyzeFile;\r
7917     break;\r
7918   case EditGame:\r
7919     nowChecked = IDM_EditGame;\r
7920     break;\r
7921   case PlayFromGameFile:\r
7922     nowChecked = IDM_LoadGame;\r
7923     break;\r
7924   case EditPosition:\r
7925     nowChecked = IDM_EditPosition;\r
7926     break;\r
7927   case Training:\r
7928     nowChecked = IDM_Training;\r
7929     break;\r
7930   case IcsPlayingWhite:\r
7931   case IcsPlayingBlack:\r
7932   case IcsObserving:\r
7933   case IcsIdle:\r
7934     nowChecked = IDM_IcsClient;\r
7935     break;\r
7936   default:\r
7937   case EndOfGame:\r
7938     nowChecked = 0;\r
7939     break;\r
7940   }\r
7941   if (prevChecked != 0)\r
7942     (void) CheckMenuItem(GetMenu(hwndMain),\r
7943                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7944   if (nowChecked != 0)\r
7945     (void) CheckMenuItem(GetMenu(hwndMain),\r
7946                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7947 \r
7948   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7949     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7950                           MF_BYCOMMAND|MF_ENABLED);\r
7951   } else {\r
7952     (void) EnableMenuItem(GetMenu(hwndMain), \r
7953                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7954   }\r
7955 \r
7956   prevChecked = nowChecked;\r
7957 \r
7958   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7959   if (appData.icsActive) {\r
7960        if (appData.icsEngineAnalyze) {\r
7961                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7962                        MF_BYCOMMAND|MF_CHECKED);\r
7963        } else {\r
7964                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7965                        MF_BYCOMMAND|MF_UNCHECKED);\r
7966        }\r
7967   }\r
7968 }\r
7969 \r
7970 VOID\r
7971 SetICSMode()\r
7972 {\r
7973   HMENU hmenu = GetMenu(hwndMain);\r
7974   SetMenuEnables(hmenu, icsEnables);\r
7975   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7976     MF_BYPOSITION|MF_ENABLED);\r
7977 #if ZIPPY\r
7978   if (appData.zippyPlay) {\r
7979     SetMenuEnables(hmenu, zippyEnables);\r
7980     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7981          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7982           MF_BYCOMMAND|MF_ENABLED);\r
7983   }\r
7984 #endif\r
7985 }\r
7986 \r
7987 VOID\r
7988 SetGNUMode()\r
7989 {\r
7990   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7991 }\r
7992 \r
7993 VOID\r
7994 SetNCPMode()\r
7995 {\r
7996   HMENU hmenu = GetMenu(hwndMain);\r
7997   SetMenuEnables(hmenu, ncpEnables);\r
7998   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7999     MF_BYPOSITION|MF_GRAYED);\r
8000     DrawMenuBar(hwndMain);\r
8001 }\r
8002 \r
8003 VOID\r
8004 SetCmailMode()\r
8005 {\r
8006   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8007 }\r
8008 \r
8009 VOID \r
8010 SetTrainingModeOn()\r
8011 {\r
8012   int i;\r
8013   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8014   for (i = 0; i < N_BUTTONS; i++) {\r
8015     if (buttonDesc[i].hwnd != NULL)\r
8016       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8017   }\r
8018   CommentPopDown();\r
8019 }\r
8020 \r
8021 VOID SetTrainingModeOff()\r
8022 {\r
8023   int i;\r
8024   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8025   for (i = 0; i < N_BUTTONS; i++) {\r
8026     if (buttonDesc[i].hwnd != NULL)\r
8027       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8028   }\r
8029 }\r
8030 \r
8031 \r
8032 VOID\r
8033 SetUserThinkingEnables()\r
8034 {\r
8035   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8036 }\r
8037 \r
8038 VOID\r
8039 SetMachineThinkingEnables()\r
8040 {\r
8041   HMENU hMenu = GetMenu(hwndMain);\r
8042   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8043 \r
8044   SetMenuEnables(hMenu, machineThinkingEnables);\r
8045 \r
8046   if (gameMode == MachinePlaysBlack) {\r
8047     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8048   } else if (gameMode == MachinePlaysWhite) {\r
8049     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8050   } else if (gameMode == TwoMachinesPlay) {\r
8051     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8052   }\r
8053 }\r
8054 \r
8055 \r
8056 VOID\r
8057 DisplayTitle(char *str)\r
8058 {\r
8059   char title[MSG_SIZ], *host;\r
8060   if (str[0] != NULLCHAR) {\r
8061     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8062   } else if (appData.icsActive) {\r
8063     if (appData.icsCommPort[0] != NULLCHAR)\r
8064       host = "ICS";\r
8065     else \r
8066       host = appData.icsHost;\r
8067       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8068   } else if (appData.noChessProgram) {\r
8069     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8070   } else {\r
8071     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8072     strcat(title, ": ");\r
8073     strcat(title, first.tidy);\r
8074   }\r
8075   SetWindowText(hwndMain, title);\r
8076 }\r
8077 \r
8078 \r
8079 VOID\r
8080 DisplayMessage(char *str1, char *str2)\r
8081 {\r
8082   HDC hdc;\r
8083   HFONT oldFont;\r
8084   int remain = MESSAGE_TEXT_MAX - 1;\r
8085   int len;\r
8086 \r
8087   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8088   messageText[0] = NULLCHAR;\r
8089   if (*str1) {\r
8090     len = strlen(str1);\r
8091     if (len > remain) len = remain;\r
8092     strncpy(messageText, str1, len);\r
8093     messageText[len] = NULLCHAR;\r
8094     remain -= len;\r
8095   }\r
8096   if (*str2 && remain >= 2) {\r
8097     if (*str1) {\r
8098       strcat(messageText, "  ");\r
8099       remain -= 2;\r
8100     }\r
8101     len = strlen(str2);\r
8102     if (len > remain) len = remain;\r
8103     strncat(messageText, str2, len);\r
8104   }\r
8105   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8106 \r
8107   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8108 \r
8109   SAYMACHINEMOVE();\r
8110 \r
8111   hdc = GetDC(hwndMain);\r
8112   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8113   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8114              &messageRect, messageText, strlen(messageText), NULL);\r
8115   (void) SelectObject(hdc, oldFont);\r
8116   (void) ReleaseDC(hwndMain, hdc);\r
8117 }\r
8118 \r
8119 VOID\r
8120 DisplayError(char *str, int error)\r
8121 {\r
8122   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8123   int len;\r
8124 \r
8125   if (error == 0) {\r
8126     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8127   } else {\r
8128     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8129                         NULL, error, LANG_NEUTRAL,\r
8130                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8131     if (len > 0) {\r
8132       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8133     } else {\r
8134       ErrorMap *em = errmap;\r
8135       while (em->err != 0 && em->err != error) em++;\r
8136       if (em->err != 0) {\r
8137         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8138       } else {\r
8139         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8140       }\r
8141     }\r
8142   }\r
8143   \r
8144   ErrorPopUp(_("Error"), buf);\r
8145 }\r
8146 \r
8147 \r
8148 VOID\r
8149 DisplayMoveError(char *str)\r
8150 {\r
8151   fromX = fromY = -1;\r
8152   ClearHighlights();\r
8153   DrawPosition(FALSE, NULL);\r
8154   if (appData.popupMoveErrors) {\r
8155     ErrorPopUp(_("Error"), str);\r
8156   } else {\r
8157     DisplayMessage(str, "");\r
8158     moveErrorMessageUp = TRUE;\r
8159   }\r
8160 }\r
8161 \r
8162 VOID\r
8163 DisplayFatalError(char *str, int error, int exitStatus)\r
8164 {\r
8165   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8166   int len;\r
8167   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8168 \r
8169   if (error != 0) {\r
8170     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8171                         NULL, error, LANG_NEUTRAL,\r
8172                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8173     if (len > 0) {\r
8174       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8175     } else {\r
8176       ErrorMap *em = errmap;\r
8177       while (em->err != 0 && em->err != error) em++;\r
8178       if (em->err != 0) {\r
8179         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8180       } else {\r
8181         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8182       }\r
8183     }\r
8184     str = buf;\r
8185   }\r
8186   if (appData.debugMode) {\r
8187     fprintf(debugFP, "%s: %s\n", label, str);\r
8188   }\r
8189   if (appData.popupExitMessage) {\r
8190     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8191                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8192   }\r
8193   ExitEvent(exitStatus);\r
8194 }\r
8195 \r
8196 \r
8197 VOID\r
8198 DisplayInformation(char *str)\r
8199 {\r
8200   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8201 }\r
8202 \r
8203 \r
8204 VOID\r
8205 DisplayNote(char *str)\r
8206 {\r
8207   ErrorPopUp(_("Note"), str);\r
8208 }\r
8209 \r
8210 \r
8211 typedef struct {\r
8212   char *title, *question, *replyPrefix;\r
8213   ProcRef pr;\r
8214 } QuestionParams;\r
8215 \r
8216 LRESULT CALLBACK\r
8217 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8218 {\r
8219   static QuestionParams *qp;\r
8220   char reply[MSG_SIZ];\r
8221   int len, err;\r
8222 \r
8223   switch (message) {\r
8224   case WM_INITDIALOG:\r
8225     qp = (QuestionParams *) lParam;\r
8226     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8227     Translate(hDlg, DLG_Question);\r
8228     SetWindowText(hDlg, qp->title);\r
8229     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8230     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8231     return FALSE;\r
8232 \r
8233   case WM_COMMAND:\r
8234     switch (LOWORD(wParam)) {\r
8235     case IDOK:\r
8236       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8237       if (*reply) strcat(reply, " ");\r
8238       len = strlen(reply);\r
8239       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8240       strcat(reply, "\n");\r
8241       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8242       EndDialog(hDlg, TRUE);\r
8243       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8244       return TRUE;\r
8245     case IDCANCEL:\r
8246       EndDialog(hDlg, FALSE);\r
8247       return TRUE;\r
8248     default:\r
8249       break;\r
8250     }\r
8251     break;\r
8252   }\r
8253   return FALSE;\r
8254 }\r
8255 \r
8256 VOID\r
8257 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8258 {\r
8259     QuestionParams qp;\r
8260     FARPROC lpProc;\r
8261     \r
8262     qp.title = title;\r
8263     qp.question = question;\r
8264     qp.replyPrefix = replyPrefix;\r
8265     qp.pr = pr;\r
8266     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8267     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8268       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8269     FreeProcInstance(lpProc);\r
8270 }\r
8271 \r
8272 /* [AS] Pick FRC position */\r
8273 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8274 {\r
8275     static int * lpIndexFRC;\r
8276     BOOL index_is_ok;\r
8277     char buf[16];\r
8278 \r
8279     switch( message )\r
8280     {\r
8281     case WM_INITDIALOG:\r
8282         lpIndexFRC = (int *) lParam;\r
8283 \r
8284         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8285         Translate(hDlg, DLG_NewGameFRC);\r
8286 \r
8287         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8288         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8289         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8290         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8291 \r
8292         break;\r
8293 \r
8294     case WM_COMMAND:\r
8295         switch( LOWORD(wParam) ) {\r
8296         case IDOK:\r
8297             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8298             EndDialog( hDlg, 0 );\r
8299             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8300             return TRUE;\r
8301         case IDCANCEL:\r
8302             EndDialog( hDlg, 1 );   \r
8303             return TRUE;\r
8304         case IDC_NFG_Edit:\r
8305             if( HIWORD(wParam) == EN_CHANGE ) {\r
8306                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8307 \r
8308                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8309             }\r
8310             return TRUE;\r
8311         case IDC_NFG_Random:\r
8312           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8313             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8314             return TRUE;\r
8315         }\r
8316 \r
8317         break;\r
8318     }\r
8319 \r
8320     return FALSE;\r
8321 }\r
8322 \r
8323 int NewGameFRC()\r
8324 {\r
8325     int result;\r
8326     int index = appData.defaultFrcPosition;\r
8327     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8328 \r
8329     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8330 \r
8331     if( result == 0 ) {\r
8332         appData.defaultFrcPosition = index;\r
8333     }\r
8334 \r
8335     return result;\r
8336 }\r
8337 \r
8338 /* [AS] Game list options. Refactored by HGM */\r
8339 \r
8340 HWND gameListOptionsDialog;\r
8341 \r
8342 // low-level front-end: clear text edit / list widget\r
8343 void\r
8344 GLT_ClearList()\r
8345 {\r
8346     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8347 }\r
8348 \r
8349 // low-level front-end: clear text edit / list widget\r
8350 void\r
8351 GLT_DeSelectList()\r
8352 {\r
8353     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8354 }\r
8355 \r
8356 // low-level front-end: append line to text edit / list widget\r
8357 void\r
8358 GLT_AddToList( char *name )\r
8359 {\r
8360     if( name != 0 ) {\r
8361             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8362     }\r
8363 }\r
8364 \r
8365 // low-level front-end: get line from text edit / list widget\r
8366 Boolean\r
8367 GLT_GetFromList( int index, char *name )\r
8368 {\r
8369     if( name != 0 ) {\r
8370             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8371                 return TRUE;\r
8372     }\r
8373     return FALSE;\r
8374 }\r
8375 \r
8376 void GLT_MoveSelection( HWND hDlg, int delta )\r
8377 {\r
8378     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8379     int idx2 = idx1 + delta;\r
8380     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8381 \r
8382     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8383         char buf[128];\r
8384 \r
8385         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8386         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8387         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8388         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8389     }\r
8390 }\r
8391 \r
8392 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8393 {\r
8394     switch( message )\r
8395     {\r
8396     case WM_INITDIALOG:\r
8397         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8398         \r
8399         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8400         Translate(hDlg, DLG_GameListOptions);\r
8401 \r
8402         /* Initialize list */\r
8403         GLT_TagsToList( lpUserGLT );\r
8404 \r
8405         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8406 \r
8407         break;\r
8408 \r
8409     case WM_COMMAND:\r
8410         switch( LOWORD(wParam) ) {\r
8411         case IDOK:\r
8412             GLT_ParseList();\r
8413             EndDialog( hDlg, 0 );\r
8414             return TRUE;\r
8415         case IDCANCEL:\r
8416             EndDialog( hDlg, 1 );\r
8417             return TRUE;\r
8418 \r
8419         case IDC_GLT_Default:\r
8420             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8421             return TRUE;\r
8422 \r
8423         case IDC_GLT_Restore:\r
8424             GLT_TagsToList( appData.gameListTags );\r
8425             return TRUE;\r
8426 \r
8427         case IDC_GLT_Up:\r
8428             GLT_MoveSelection( hDlg, -1 );\r
8429             return TRUE;\r
8430 \r
8431         case IDC_GLT_Down:\r
8432             GLT_MoveSelection( hDlg, +1 );\r
8433             return TRUE;\r
8434         }\r
8435 \r
8436         break;\r
8437     }\r
8438 \r
8439     return FALSE;\r
8440 }\r
8441 \r
8442 int GameListOptions()\r
8443 {\r
8444     int result;\r
8445     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8446 \r
8447       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8448 \r
8449     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8450 \r
8451     if( result == 0 ) {\r
8452         /* [AS] Memory leak here! */\r
8453         appData.gameListTags = strdup( lpUserGLT ); \r
8454     }\r
8455 \r
8456     return result;\r
8457 }\r
8458 \r
8459 VOID\r
8460 DisplayIcsInteractionTitle(char *str)\r
8461 {\r
8462   char consoleTitle[MSG_SIZ];\r
8463 \r
8464     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8465   SetWindowText(hwndConsole, consoleTitle);\r
8466 }\r
8467 \r
8468 void\r
8469 DrawPosition(int fullRedraw, Board board)\r
8470 {\r
8471   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8472 }\r
8473 \r
8474 void NotifyFrontendLogin()\r
8475 {\r
8476         if (hwndConsole)\r
8477                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8478 }\r
8479 \r
8480 VOID\r
8481 ResetFrontEnd()\r
8482 {\r
8483   fromX = fromY = -1;\r
8484   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8485     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8486     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8487     dragInfo.lastpos = dragInfo.pos;\r
8488     dragInfo.start.x = dragInfo.start.y = -1;\r
8489     dragInfo.from = dragInfo.start;\r
8490     ReleaseCapture();\r
8491     DrawPosition(TRUE, NULL);\r
8492   }\r
8493   TagsPopDown();\r
8494 }\r
8495 \r
8496 \r
8497 VOID\r
8498 CommentPopUp(char *title, char *str)\r
8499 {\r
8500   HWND hwnd = GetActiveWindow();\r
8501   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8502   SAY(str);\r
8503   SetActiveWindow(hwnd);\r
8504 }\r
8505 \r
8506 VOID\r
8507 CommentPopDown(void)\r
8508 {\r
8509   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8510   if (commentDialog) {\r
8511     ShowWindow(commentDialog, SW_HIDE);\r
8512   }\r
8513   commentUp = FALSE;\r
8514 }\r
8515 \r
8516 VOID\r
8517 EditCommentPopUp(int index, char *title, char *str)\r
8518 {\r
8519   EitherCommentPopUp(index, title, str, TRUE);\r
8520 }\r
8521 \r
8522 \r
8523 VOID\r
8524 RingBell()\r
8525 {\r
8526   MyPlaySound(&sounds[(int)SoundMove]);\r
8527 }\r
8528 \r
8529 VOID PlayIcsWinSound()\r
8530 {\r
8531   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8532 }\r
8533 \r
8534 VOID PlayIcsLossSound()\r
8535 {\r
8536   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8537 }\r
8538 \r
8539 VOID PlayIcsDrawSound()\r
8540 {\r
8541   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8542 }\r
8543 \r
8544 VOID PlayIcsUnfinishedSound()\r
8545 {\r
8546   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8547 }\r
8548 \r
8549 VOID\r
8550 PlayAlarmSound()\r
8551 {\r
8552   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8553 }\r
8554 \r
8555 \r
8556 VOID\r
8557 EchoOn()\r
8558 {\r
8559   HWND hInput;\r
8560   consoleEcho = TRUE;\r
8561   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8562   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8563   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8564 }\r
8565 \r
8566 \r
8567 VOID\r
8568 EchoOff()\r
8569 {\r
8570   CHARFORMAT cf;\r
8571   HWND hInput;\r
8572   consoleEcho = FALSE;\r
8573   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8574   /* This works OK: set text and background both to the same color */\r
8575   cf = consoleCF;\r
8576   cf.crTextColor = COLOR_ECHOOFF;\r
8577   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8578   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8579 }\r
8580 \r
8581 /* No Raw()...? */\r
8582 \r
8583 void Colorize(ColorClass cc, int continuation)\r
8584 {\r
8585   currentColorClass = cc;\r
8586   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8587   consoleCF.crTextColor = textAttribs[cc].color;\r
8588   consoleCF.dwEffects = textAttribs[cc].effects;\r
8589   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8590 }\r
8591 \r
8592 char *\r
8593 UserName()\r
8594 {\r
8595   static char buf[MSG_SIZ];\r
8596   DWORD bufsiz = MSG_SIZ;\r
8597 \r
8598   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8599         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8600   }\r
8601   if (!GetUserName(buf, &bufsiz)) {\r
8602     /*DisplayError("Error getting user name", GetLastError());*/\r
8603     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8604   }\r
8605   return buf;\r
8606 }\r
8607 \r
8608 char *\r
8609 HostName()\r
8610 {\r
8611   static char buf[MSG_SIZ];\r
8612   DWORD bufsiz = MSG_SIZ;\r
8613 \r
8614   if (!GetComputerName(buf, &bufsiz)) {\r
8615     /*DisplayError("Error getting host name", GetLastError());*/\r
8616     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8617   }\r
8618   return buf;\r
8619 }\r
8620 \r
8621 \r
8622 int\r
8623 ClockTimerRunning()\r
8624 {\r
8625   return clockTimerEvent != 0;\r
8626 }\r
8627 \r
8628 int\r
8629 StopClockTimer()\r
8630 {\r
8631   if (clockTimerEvent == 0) return FALSE;\r
8632   KillTimer(hwndMain, clockTimerEvent);\r
8633   clockTimerEvent = 0;\r
8634   return TRUE;\r
8635 }\r
8636 \r
8637 void\r
8638 StartClockTimer(long millisec)\r
8639 {\r
8640   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8641                              (UINT) millisec, NULL);\r
8642 }\r
8643 \r
8644 void\r
8645 DisplayWhiteClock(long timeRemaining, int highlight)\r
8646 {\r
8647   HDC hdc;\r
8648   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8649 \r
8650   if(appData.noGUI) return;\r
8651   hdc = GetDC(hwndMain);\r
8652   if (!IsIconic(hwndMain)) {\r
8653     DisplayAClock(hdc, timeRemaining, highlight, \r
8654                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8655   }\r
8656   if (highlight && iconCurrent == iconBlack) {\r
8657     iconCurrent = iconWhite;\r
8658     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8659     if (IsIconic(hwndMain)) {\r
8660       DrawIcon(hdc, 2, 2, iconCurrent);\r
8661     }\r
8662   }\r
8663   (void) ReleaseDC(hwndMain, hdc);\r
8664   if (hwndConsole)\r
8665     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8666 }\r
8667 \r
8668 void\r
8669 DisplayBlackClock(long timeRemaining, int highlight)\r
8670 {\r
8671   HDC hdc;\r
8672   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8673 \r
8674   if(appData.noGUI) return;\r
8675   hdc = GetDC(hwndMain);\r
8676   if (!IsIconic(hwndMain)) {\r
8677     DisplayAClock(hdc, timeRemaining, highlight, \r
8678                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8679   }\r
8680   if (highlight && iconCurrent == iconWhite) {\r
8681     iconCurrent = iconBlack;\r
8682     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8683     if (IsIconic(hwndMain)) {\r
8684       DrawIcon(hdc, 2, 2, iconCurrent);\r
8685     }\r
8686   }\r
8687   (void) ReleaseDC(hwndMain, hdc);\r
8688   if (hwndConsole)\r
8689     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8690 }\r
8691 \r
8692 \r
8693 int\r
8694 LoadGameTimerRunning()\r
8695 {\r
8696   return loadGameTimerEvent != 0;\r
8697 }\r
8698 \r
8699 int\r
8700 StopLoadGameTimer()\r
8701 {\r
8702   if (loadGameTimerEvent == 0) return FALSE;\r
8703   KillTimer(hwndMain, loadGameTimerEvent);\r
8704   loadGameTimerEvent = 0;\r
8705   return TRUE;\r
8706 }\r
8707 \r
8708 void\r
8709 StartLoadGameTimer(long millisec)\r
8710 {\r
8711   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8712                                 (UINT) millisec, NULL);\r
8713 }\r
8714 \r
8715 void\r
8716 AutoSaveGame()\r
8717 {\r
8718   char *defName;\r
8719   FILE *f;\r
8720   char fileTitle[MSG_SIZ];\r
8721 \r
8722   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8723   f = OpenFileDialog(hwndMain, "a", defName,\r
8724                      appData.oldSaveStyle ? "gam" : "pgn",\r
8725                      GAME_FILT, \r
8726                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8727   if (f != NULL) {\r
8728     SaveGame(f, 0, "");\r
8729     fclose(f);\r
8730   }\r
8731 }\r
8732 \r
8733 \r
8734 void\r
8735 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8736 {\r
8737   if (delayedTimerEvent != 0) {\r
8738     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8739       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8740     }\r
8741     KillTimer(hwndMain, delayedTimerEvent);\r
8742     delayedTimerEvent = 0;\r
8743     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8744     delayedTimerCallback();\r
8745   }\r
8746   delayedTimerCallback = cb;\r
8747   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8748                                 (UINT) millisec, NULL);\r
8749 }\r
8750 \r
8751 DelayedEventCallback\r
8752 GetDelayedEvent()\r
8753 {\r
8754   if (delayedTimerEvent) {\r
8755     return delayedTimerCallback;\r
8756   } else {\r
8757     return NULL;\r
8758   }\r
8759 }\r
8760 \r
8761 void\r
8762 CancelDelayedEvent()\r
8763 {\r
8764   if (delayedTimerEvent) {\r
8765     KillTimer(hwndMain, delayedTimerEvent);\r
8766     delayedTimerEvent = 0;\r
8767   }\r
8768 }\r
8769 \r
8770 DWORD GetWin32Priority(int nice)\r
8771 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8772 /*\r
8773 REALTIME_PRIORITY_CLASS     0x00000100\r
8774 HIGH_PRIORITY_CLASS         0x00000080\r
8775 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8776 NORMAL_PRIORITY_CLASS       0x00000020\r
8777 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8778 IDLE_PRIORITY_CLASS         0x00000040\r
8779 */\r
8780         if (nice < -15) return 0x00000080;\r
8781         if (nice < 0)   return 0x00008000;\r
8782         if (nice == 0)  return 0x00000020;\r
8783         if (nice < 15)  return 0x00004000;\r
8784         return 0x00000040;\r
8785 }\r
8786 \r
8787 /* Start a child process running the given program.\r
8788    The process's standard output can be read from "from", and its\r
8789    standard input can be written to "to".\r
8790    Exit with fatal error if anything goes wrong.\r
8791    Returns an opaque pointer that can be used to destroy the process\r
8792    later.\r
8793 */\r
8794 int\r
8795 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8796 {\r
8797 #define BUFSIZE 4096\r
8798 \r
8799   HANDLE hChildStdinRd, hChildStdinWr,\r
8800     hChildStdoutRd, hChildStdoutWr;\r
8801   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8802   SECURITY_ATTRIBUTES saAttr;\r
8803   BOOL fSuccess;\r
8804   PROCESS_INFORMATION piProcInfo;\r
8805   STARTUPINFO siStartInfo;\r
8806   ChildProc *cp;\r
8807   char buf[MSG_SIZ];\r
8808   DWORD err;\r
8809 \r
8810   if (appData.debugMode) {\r
8811     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8812   }\r
8813 \r
8814   *pr = NoProc;\r
8815 \r
8816   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8817   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8818   saAttr.bInheritHandle = TRUE;\r
8819   saAttr.lpSecurityDescriptor = NULL;\r
8820 \r
8821   /*\r
8822    * The steps for redirecting child's STDOUT:\r
8823    *     1. Create anonymous pipe to be STDOUT for child.\r
8824    *     2. Create a noninheritable duplicate of read handle,\r
8825    *         and close the inheritable read handle.\r
8826    */\r
8827 \r
8828   /* Create a pipe for the child's STDOUT. */\r
8829   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8830     return GetLastError();\r
8831   }\r
8832 \r
8833   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8834   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8835                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8836                              FALSE,     /* not inherited */\r
8837                              DUPLICATE_SAME_ACCESS);\r
8838   if (! fSuccess) {\r
8839     return GetLastError();\r
8840   }\r
8841   CloseHandle(hChildStdoutRd);\r
8842 \r
8843   /*\r
8844    * The steps for redirecting child's STDIN:\r
8845    *     1. Create anonymous pipe to be STDIN for child.\r
8846    *     2. Create a noninheritable duplicate of write handle,\r
8847    *         and close the inheritable write handle.\r
8848    */\r
8849 \r
8850   /* Create a pipe for the child's STDIN. */\r
8851   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8852     return GetLastError();\r
8853   }\r
8854 \r
8855   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8856   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8857                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8858                              FALSE,     /* not inherited */\r
8859                              DUPLICATE_SAME_ACCESS);\r
8860   if (! fSuccess) {\r
8861     return GetLastError();\r
8862   }\r
8863   CloseHandle(hChildStdinWr);\r
8864 \r
8865   /* Arrange to (1) look in dir for the child .exe file, and\r
8866    * (2) have dir be the child's working directory.  Interpret\r
8867    * dir relative to the directory WinBoard loaded from. */\r
8868   GetCurrentDirectory(MSG_SIZ, buf);\r
8869   SetCurrentDirectory(installDir);\r
8870   SetCurrentDirectory(dir);\r
8871 \r
8872   /* Now create the child process. */\r
8873 \r
8874   siStartInfo.cb = sizeof(STARTUPINFO);\r
8875   siStartInfo.lpReserved = NULL;\r
8876   siStartInfo.lpDesktop = NULL;\r
8877   siStartInfo.lpTitle = NULL;\r
8878   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8879   siStartInfo.cbReserved2 = 0;\r
8880   siStartInfo.lpReserved2 = NULL;\r
8881   siStartInfo.hStdInput = hChildStdinRd;\r
8882   siStartInfo.hStdOutput = hChildStdoutWr;\r
8883   siStartInfo.hStdError = hChildStdoutWr;\r
8884 \r
8885   fSuccess = CreateProcess(NULL,\r
8886                            cmdLine,        /* command line */\r
8887                            NULL,           /* process security attributes */\r
8888                            NULL,           /* primary thread security attrs */\r
8889                            TRUE,           /* handles are inherited */\r
8890                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8891                            NULL,           /* use parent's environment */\r
8892                            NULL,\r
8893                            &siStartInfo, /* STARTUPINFO pointer */\r
8894                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8895 \r
8896   err = GetLastError();\r
8897   SetCurrentDirectory(buf); /* return to prev directory */\r
8898   if (! fSuccess) {\r
8899     return err;\r
8900   }\r
8901 \r
8902   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8903     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8904     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8905   }\r
8906 \r
8907   /* Close the handles we don't need in the parent */\r
8908   CloseHandle(piProcInfo.hThread);\r
8909   CloseHandle(hChildStdinRd);\r
8910   CloseHandle(hChildStdoutWr);\r
8911 \r
8912   /* Prepare return value */\r
8913   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8914   cp->kind = CPReal;\r
8915   cp->hProcess = piProcInfo.hProcess;\r
8916   cp->pid = piProcInfo.dwProcessId;\r
8917   cp->hFrom = hChildStdoutRdDup;\r
8918   cp->hTo = hChildStdinWrDup;\r
8919 \r
8920   *pr = (void *) cp;\r
8921 \r
8922   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8923      2000 where engines sometimes don't see the initial command(s)\r
8924      from WinBoard and hang.  I don't understand how that can happen,\r
8925      but the Sleep is harmless, so I've put it in.  Others have also\r
8926      reported what may be the same problem, so hopefully this will fix\r
8927      it for them too.  */\r
8928   Sleep(500);\r
8929 \r
8930   return NO_ERROR;\r
8931 }\r
8932 \r
8933 \r
8934 void\r
8935 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8936 {\r
8937   ChildProc *cp; int result;\r
8938 \r
8939   cp = (ChildProc *) pr;\r
8940   if (cp == NULL) return;\r
8941 \r
8942   switch (cp->kind) {\r
8943   case CPReal:\r
8944     /* TerminateProcess is considered harmful, so... */\r
8945     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8946     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8947     /* The following doesn't work because the chess program\r
8948        doesn't "have the same console" as WinBoard.  Maybe\r
8949        we could arrange for this even though neither WinBoard\r
8950        nor the chess program uses a console for stdio? */\r
8951     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8952 \r
8953     /* [AS] Special termination modes for misbehaving programs... */\r
8954     if( signal == 9 ) { \r
8955         result = TerminateProcess( cp->hProcess, 0 );\r
8956 \r
8957         if ( appData.debugMode) {\r
8958             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8959         }\r
8960     }\r
8961     else if( signal == 10 ) {\r
8962         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8963 \r
8964         if( dw != WAIT_OBJECT_0 ) {\r
8965             result = TerminateProcess( cp->hProcess, 0 );\r
8966 \r
8967             if ( appData.debugMode) {\r
8968                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8969             }\r
8970 \r
8971         }\r
8972     }\r
8973 \r
8974     CloseHandle(cp->hProcess);\r
8975     break;\r
8976 \r
8977   case CPComm:\r
8978     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8979     break;\r
8980 \r
8981   case CPSock:\r
8982     closesocket(cp->sock);\r
8983     WSACleanup();\r
8984     break;\r
8985 \r
8986   case CPRcmd:\r
8987     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8988     closesocket(cp->sock);\r
8989     closesocket(cp->sock2);\r
8990     WSACleanup();\r
8991     break;\r
8992   }\r
8993   free(cp);\r
8994 }\r
8995 \r
8996 void\r
8997 InterruptChildProcess(ProcRef pr)\r
8998 {\r
8999   ChildProc *cp;\r
9000 \r
9001   cp = (ChildProc *) pr;\r
9002   if (cp == NULL) return;\r
9003   switch (cp->kind) {\r
9004   case CPReal:\r
9005     /* The following doesn't work because the chess program\r
9006        doesn't "have the same console" as WinBoard.  Maybe\r
9007        we could arrange for this even though neither WinBoard\r
9008        nor the chess program uses a console for stdio */\r
9009     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9010     break;\r
9011 \r
9012   case CPComm:\r
9013   case CPSock:\r
9014     /* Can't interrupt */\r
9015     break;\r
9016 \r
9017   case CPRcmd:\r
9018     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9019     break;\r
9020   }\r
9021 }\r
9022 \r
9023 \r
9024 int\r
9025 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9026 {\r
9027   char cmdLine[MSG_SIZ];\r
9028 \r
9029   if (port[0] == NULLCHAR) {\r
9030     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9031   } else {\r
9032     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9033   }\r
9034   return StartChildProcess(cmdLine, "", pr);\r
9035 }\r
9036 \r
9037 \r
9038 /* Code to open TCP sockets */\r
9039 \r
9040 int\r
9041 OpenTCP(char *host, char *port, ProcRef *pr)\r
9042 {\r
9043   ChildProc *cp;\r
9044   int err;\r
9045   SOCKET s;\r
9046   struct sockaddr_in sa, mysa;\r
9047   struct hostent FAR *hp;\r
9048   unsigned short uport;\r
9049   WORD wVersionRequested;\r
9050   WSADATA wsaData;\r
9051 \r
9052   /* Initialize socket DLL */\r
9053   wVersionRequested = MAKEWORD(1, 1);\r
9054   err = WSAStartup(wVersionRequested, &wsaData);\r
9055   if (err != 0) return err;\r
9056 \r
9057   /* Make socket */\r
9058   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9059     err = WSAGetLastError();\r
9060     WSACleanup();\r
9061     return err;\r
9062   }\r
9063 \r
9064   /* Bind local address using (mostly) don't-care values.\r
9065    */\r
9066   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9067   mysa.sin_family = AF_INET;\r
9068   mysa.sin_addr.s_addr = INADDR_ANY;\r
9069   uport = (unsigned short) 0;\r
9070   mysa.sin_port = htons(uport);\r
9071   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9072       == SOCKET_ERROR) {\r
9073     err = WSAGetLastError();\r
9074     WSACleanup();\r
9075     return err;\r
9076   }\r
9077 \r
9078   /* Resolve remote host name */\r
9079   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9080   if (!(hp = gethostbyname(host))) {\r
9081     unsigned int b0, b1, b2, b3;\r
9082 \r
9083     err = WSAGetLastError();\r
9084 \r
9085     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9086       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9087       hp->h_addrtype = AF_INET;\r
9088       hp->h_length = 4;\r
9089       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9090       hp->h_addr_list[0] = (char *) malloc(4);\r
9091       hp->h_addr_list[0][0] = (char) b0;\r
9092       hp->h_addr_list[0][1] = (char) b1;\r
9093       hp->h_addr_list[0][2] = (char) b2;\r
9094       hp->h_addr_list[0][3] = (char) b3;\r
9095     } else {\r
9096       WSACleanup();\r
9097       return err;\r
9098     }\r
9099   }\r
9100   sa.sin_family = hp->h_addrtype;\r
9101   uport = (unsigned short) atoi(port);\r
9102   sa.sin_port = htons(uport);\r
9103   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9104 \r
9105   /* Make connection */\r
9106   if (connect(s, (struct sockaddr *) &sa,\r
9107               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9108     err = WSAGetLastError();\r
9109     WSACleanup();\r
9110     return err;\r
9111   }\r
9112 \r
9113   /* Prepare return value */\r
9114   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9115   cp->kind = CPSock;\r
9116   cp->sock = s;\r
9117   *pr = (ProcRef *) cp;\r
9118 \r
9119   return NO_ERROR;\r
9120 }\r
9121 \r
9122 int\r
9123 OpenCommPort(char *name, ProcRef *pr)\r
9124 {\r
9125   HANDLE h;\r
9126   COMMTIMEOUTS ct;\r
9127   ChildProc *cp;\r
9128   char fullname[MSG_SIZ];\r
9129 \r
9130   if (*name != '\\')\r
9131     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9132   else\r
9133     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9134 \r
9135   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9136                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9137   if (h == (HANDLE) -1) {\r
9138     return GetLastError();\r
9139   }\r
9140   hCommPort = h;\r
9141 \r
9142   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9143 \r
9144   /* Accumulate characters until a 100ms pause, then parse */\r
9145   ct.ReadIntervalTimeout = 100;\r
9146   ct.ReadTotalTimeoutMultiplier = 0;\r
9147   ct.ReadTotalTimeoutConstant = 0;\r
9148   ct.WriteTotalTimeoutMultiplier = 0;\r
9149   ct.WriteTotalTimeoutConstant = 0;\r
9150   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9151 \r
9152   /* Prepare return value */\r
9153   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9154   cp->kind = CPComm;\r
9155   cp->hFrom = h;\r
9156   cp->hTo = h;\r
9157   *pr = (ProcRef *) cp;\r
9158 \r
9159   return NO_ERROR;\r
9160 }\r
9161 \r
9162 int\r
9163 OpenLoopback(ProcRef *pr)\r
9164 {\r
9165   DisplayFatalError(_("Not implemented"), 0, 1);\r
9166   return NO_ERROR;\r
9167 }\r
9168 \r
9169 \r
9170 int\r
9171 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9172 {\r
9173   ChildProc *cp;\r
9174   int err;\r
9175   SOCKET s, s2, s3;\r
9176   struct sockaddr_in sa, mysa;\r
9177   struct hostent FAR *hp;\r
9178   unsigned short uport;\r
9179   WORD wVersionRequested;\r
9180   WSADATA wsaData;\r
9181   int fromPort;\r
9182   char stderrPortStr[MSG_SIZ];\r
9183 \r
9184   /* Initialize socket DLL */\r
9185   wVersionRequested = MAKEWORD(1, 1);\r
9186   err = WSAStartup(wVersionRequested, &wsaData);\r
9187   if (err != 0) return err;\r
9188 \r
9189   /* Resolve remote host name */\r
9190   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9191   if (!(hp = gethostbyname(host))) {\r
9192     unsigned int b0, b1, b2, b3;\r
9193 \r
9194     err = WSAGetLastError();\r
9195 \r
9196     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9197       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9198       hp->h_addrtype = AF_INET;\r
9199       hp->h_length = 4;\r
9200       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9201       hp->h_addr_list[0] = (char *) malloc(4);\r
9202       hp->h_addr_list[0][0] = (char) b0;\r
9203       hp->h_addr_list[0][1] = (char) b1;\r
9204       hp->h_addr_list[0][2] = (char) b2;\r
9205       hp->h_addr_list[0][3] = (char) b3;\r
9206     } else {\r
9207       WSACleanup();\r
9208       return err;\r
9209     }\r
9210   }\r
9211   sa.sin_family = hp->h_addrtype;\r
9212   uport = (unsigned short) 514;\r
9213   sa.sin_port = htons(uport);\r
9214   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9215 \r
9216   /* Bind local socket to unused "privileged" port address\r
9217    */\r
9218   s = INVALID_SOCKET;\r
9219   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9220   mysa.sin_family = AF_INET;\r
9221   mysa.sin_addr.s_addr = INADDR_ANY;\r
9222   for (fromPort = 1023;; fromPort--) {\r
9223     if (fromPort < 0) {\r
9224       WSACleanup();\r
9225       return WSAEADDRINUSE;\r
9226     }\r
9227     if (s == INVALID_SOCKET) {\r
9228       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9229         err = WSAGetLastError();\r
9230         WSACleanup();\r
9231         return err;\r
9232       }\r
9233     }\r
9234     uport = (unsigned short) fromPort;\r
9235     mysa.sin_port = htons(uport);\r
9236     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9237         == SOCKET_ERROR) {\r
9238       err = WSAGetLastError();\r
9239       if (err == WSAEADDRINUSE) continue;\r
9240       WSACleanup();\r
9241       return err;\r
9242     }\r
9243     if (connect(s, (struct sockaddr *) &sa,\r
9244       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9245       err = WSAGetLastError();\r
9246       if (err == WSAEADDRINUSE) {\r
9247         closesocket(s);\r
9248         s = -1;\r
9249         continue;\r
9250       }\r
9251       WSACleanup();\r
9252       return err;\r
9253     }\r
9254     break;\r
9255   }\r
9256 \r
9257   /* Bind stderr local socket to unused "privileged" port address\r
9258    */\r
9259   s2 = INVALID_SOCKET;\r
9260   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9261   mysa.sin_family = AF_INET;\r
9262   mysa.sin_addr.s_addr = INADDR_ANY;\r
9263   for (fromPort = 1023;; fromPort--) {\r
9264     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9265     if (fromPort < 0) {\r
9266       (void) closesocket(s);\r
9267       WSACleanup();\r
9268       return WSAEADDRINUSE;\r
9269     }\r
9270     if (s2 == INVALID_SOCKET) {\r
9271       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9272         err = WSAGetLastError();\r
9273         closesocket(s);\r
9274         WSACleanup();\r
9275         return err;\r
9276       }\r
9277     }\r
9278     uport = (unsigned short) fromPort;\r
9279     mysa.sin_port = htons(uport);\r
9280     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9281         == SOCKET_ERROR) {\r
9282       err = WSAGetLastError();\r
9283       if (err == WSAEADDRINUSE) continue;\r
9284       (void) closesocket(s);\r
9285       WSACleanup();\r
9286       return err;\r
9287     }\r
9288     if (listen(s2, 1) == SOCKET_ERROR) {\r
9289       err = WSAGetLastError();\r
9290       if (err == WSAEADDRINUSE) {\r
9291         closesocket(s2);\r
9292         s2 = INVALID_SOCKET;\r
9293         continue;\r
9294       }\r
9295       (void) closesocket(s);\r
9296       (void) closesocket(s2);\r
9297       WSACleanup();\r
9298       return err;\r
9299     }\r
9300     break;\r
9301   }\r
9302   prevStderrPort = fromPort; // remember port used\r
9303   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9304 \r
9305   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9306     err = WSAGetLastError();\r
9307     (void) closesocket(s);\r
9308     (void) closesocket(s2);\r
9309     WSACleanup();\r
9310     return err;\r
9311   }\r
9312 \r
9313   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9314     err = WSAGetLastError();\r
9315     (void) closesocket(s);\r
9316     (void) closesocket(s2);\r
9317     WSACleanup();\r
9318     return err;\r
9319   }\r
9320   if (*user == NULLCHAR) user = UserName();\r
9321   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9322     err = WSAGetLastError();\r
9323     (void) closesocket(s);\r
9324     (void) closesocket(s2);\r
9325     WSACleanup();\r
9326     return err;\r
9327   }\r
9328   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9329     err = WSAGetLastError();\r
9330     (void) closesocket(s);\r
9331     (void) closesocket(s2);\r
9332     WSACleanup();\r
9333     return err;\r
9334   }\r
9335 \r
9336   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9337     err = WSAGetLastError();\r
9338     (void) closesocket(s);\r
9339     (void) closesocket(s2);\r
9340     WSACleanup();\r
9341     return err;\r
9342   }\r
9343   (void) closesocket(s2);  /* Stop listening */\r
9344 \r
9345   /* Prepare return value */\r
9346   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9347   cp->kind = CPRcmd;\r
9348   cp->sock = s;\r
9349   cp->sock2 = s3;\r
9350   *pr = (ProcRef *) cp;\r
9351 \r
9352   return NO_ERROR;\r
9353 }\r
9354 \r
9355 \r
9356 InputSourceRef\r
9357 AddInputSource(ProcRef pr, int lineByLine,\r
9358                InputCallback func, VOIDSTAR closure)\r
9359 {\r
9360   InputSource *is, *is2 = NULL;\r
9361   ChildProc *cp = (ChildProc *) pr;\r
9362 \r
9363   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9364   is->lineByLine = lineByLine;\r
9365   is->func = func;\r
9366   is->closure = closure;\r
9367   is->second = NULL;\r
9368   is->next = is->buf;\r
9369   if (pr == NoProc) {\r
9370     is->kind = CPReal;\r
9371     consoleInputSource = is;\r
9372   } else {\r
9373     is->kind = cp->kind;\r
9374     /* \r
9375         [AS] Try to avoid a race condition if the thread is given control too early:\r
9376         we create all threads suspended so that the is->hThread variable can be\r
9377         safely assigned, then let the threads start with ResumeThread.\r
9378     */\r
9379     switch (cp->kind) {\r
9380     case CPReal:\r
9381       is->hFile = cp->hFrom;\r
9382       cp->hFrom = NULL; /* now owned by InputThread */\r
9383       is->hThread =\r
9384         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9385                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9386       break;\r
9387 \r
9388     case CPComm:\r
9389       is->hFile = cp->hFrom;\r
9390       cp->hFrom = NULL; /* now owned by InputThread */\r
9391       is->hThread =\r
9392         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9393                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9394       break;\r
9395 \r
9396     case CPSock:\r
9397       is->sock = cp->sock;\r
9398       is->hThread =\r
9399         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9400                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9401       break;\r
9402 \r
9403     case CPRcmd:\r
9404       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9405       *is2 = *is;\r
9406       is->sock = cp->sock;\r
9407       is->second = is2;\r
9408       is2->sock = cp->sock2;\r
9409       is2->second = is2;\r
9410       is->hThread =\r
9411         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9412                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9413       is2->hThread =\r
9414         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9415                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9416       break;\r
9417     }\r
9418 \r
9419     if( is->hThread != NULL ) {\r
9420         ResumeThread( is->hThread );\r
9421     }\r
9422 \r
9423     if( is2 != NULL && is2->hThread != NULL ) {\r
9424         ResumeThread( is2->hThread );\r
9425     }\r
9426   }\r
9427 \r
9428   return (InputSourceRef) is;\r
9429 }\r
9430 \r
9431 void\r
9432 RemoveInputSource(InputSourceRef isr)\r
9433 {\r
9434   InputSource *is;\r
9435 \r
9436   is = (InputSource *) isr;\r
9437   is->hThread = NULL;  /* tell thread to stop */\r
9438   CloseHandle(is->hThread);\r
9439   if (is->second != NULL) {\r
9440     is->second->hThread = NULL;\r
9441     CloseHandle(is->second->hThread);\r
9442   }\r
9443 }\r
9444 \r
9445 int no_wrap(char *message, int count)\r
9446 {\r
9447     ConsoleOutput(message, count, FALSE);\r
9448     return count;\r
9449 }\r
9450 \r
9451 int\r
9452 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9453 {\r
9454   DWORD dOutCount;\r
9455   int outCount = SOCKET_ERROR;\r
9456   ChildProc *cp = (ChildProc *) pr;\r
9457   static OVERLAPPED ovl;\r
9458   static int line = 0;\r
9459 \r
9460   if (pr == NoProc)\r
9461   {\r
9462     if (appData.noJoin || !appData.useInternalWrap)\r
9463       return no_wrap(message, count);\r
9464     else\r
9465     {\r
9466       int width = get_term_width();\r
9467       int len = wrap(NULL, message, count, width, &line);\r
9468       char *msg = malloc(len);\r
9469       int dbgchk;\r
9470 \r
9471       if (!msg)\r
9472         return no_wrap(message, count);\r
9473       else\r
9474       {\r
9475         dbgchk = wrap(msg, message, count, width, &line);\r
9476         if (dbgchk != len && appData.debugMode)\r
9477             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9478         ConsoleOutput(msg, len, FALSE);\r
9479         free(msg);\r
9480         return len;\r
9481       }\r
9482     }\r
9483   }\r
9484 \r
9485   if (ovl.hEvent == NULL) {\r
9486     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9487   }\r
9488   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9489 \r
9490   switch (cp->kind) {\r
9491   case CPSock:\r
9492   case CPRcmd:\r
9493     outCount = send(cp->sock, message, count, 0);\r
9494     if (outCount == SOCKET_ERROR) {\r
9495       *outError = WSAGetLastError();\r
9496     } else {\r
9497       *outError = NO_ERROR;\r
9498     }\r
9499     break;\r
9500 \r
9501   case CPReal:\r
9502     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9503                   &dOutCount, NULL)) {\r
9504       *outError = NO_ERROR;\r
9505       outCount = (int) dOutCount;\r
9506     } else {\r
9507       *outError = GetLastError();\r
9508     }\r
9509     break;\r
9510 \r
9511   case CPComm:\r
9512     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9513                             &dOutCount, &ovl);\r
9514     if (*outError == NO_ERROR) {\r
9515       outCount = (int) dOutCount;\r
9516     }\r
9517     break;\r
9518   }\r
9519   return outCount;\r
9520 }\r
9521 \r
9522 int\r
9523 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9524                        long msdelay)\r
9525 {\r
9526   /* Ignore delay, not implemented for WinBoard */\r
9527   return OutputToProcess(pr, message, count, outError);\r
9528 }\r
9529 \r
9530 \r
9531 void\r
9532 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9533                         char *buf, int count, int error)\r
9534 {\r
9535   DisplayFatalError(_("Not implemented"), 0, 1);\r
9536 }\r
9537 \r
9538 /* see wgamelist.c for Game List functions */\r
9539 /* see wedittags.c for Edit Tags functions */\r
9540 \r
9541 \r
9542 VOID\r
9543 ICSInitScript()\r
9544 {\r
9545   FILE *f;\r
9546   char buf[MSG_SIZ];\r
9547   char *dummy;\r
9548 \r
9549   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9550     f = fopen(buf, "r");\r
9551     if (f != NULL) {\r
9552       ProcessICSInitScript(f);\r
9553       fclose(f);\r
9554     }\r
9555   }\r
9556 }\r
9557 \r
9558 \r
9559 VOID\r
9560 StartAnalysisClock()\r
9561 {\r
9562   if (analysisTimerEvent) return;\r
9563   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9564                                         (UINT) 2000, NULL);\r
9565 }\r
9566 \r
9567 VOID\r
9568 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9569 {\r
9570   highlightInfo.sq[0].x = fromX;\r
9571   highlightInfo.sq[0].y = fromY;\r
9572   highlightInfo.sq[1].x = toX;\r
9573   highlightInfo.sq[1].y = toY;\r
9574 }\r
9575 \r
9576 VOID\r
9577 ClearHighlights()\r
9578 {\r
9579   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9580     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9581 }\r
9582 \r
9583 VOID\r
9584 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9585 {\r
9586   premoveHighlightInfo.sq[0].x = fromX;\r
9587   premoveHighlightInfo.sq[0].y = fromY;\r
9588   premoveHighlightInfo.sq[1].x = toX;\r
9589   premoveHighlightInfo.sq[1].y = toY;\r
9590 }\r
9591 \r
9592 VOID\r
9593 ClearPremoveHighlights()\r
9594 {\r
9595   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9596     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9597 }\r
9598 \r
9599 VOID\r
9600 ShutDownFrontEnd()\r
9601 {\r
9602   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9603   DeleteClipboardTempFiles();\r
9604 }\r
9605 \r
9606 void\r
9607 BoardToTop()\r
9608 {\r
9609     if (IsIconic(hwndMain))\r
9610       ShowWindow(hwndMain, SW_RESTORE);\r
9611 \r
9612     SetActiveWindow(hwndMain);\r
9613 }\r
9614 \r
9615 /*\r
9616  * Prototypes for animation support routines\r
9617  */\r
9618 static void ScreenSquare(int column, int row, POINT * pt);\r
9619 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9620      POINT frames[], int * nFrames);\r
9621 \r
9622 \r
9623 #define kFactor 4\r
9624 \r
9625 void\r
9626 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9627 {       // [HGM] atomic: animate blast wave\r
9628         int i;\r
9629 \r
9630         explodeInfo.fromX = fromX;\r
9631         explodeInfo.fromY = fromY;\r
9632         explodeInfo.toX = toX;\r
9633         explodeInfo.toY = toY;\r
9634         for(i=1; i<4*kFactor; i++) {\r
9635             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9636             DrawPosition(FALSE, board);\r
9637             Sleep(appData.animSpeed);\r
9638         }\r
9639         explodeInfo.radius = 0;\r
9640         DrawPosition(TRUE, board);\r
9641 }\r
9642 \r
9643 void\r
9644 AnimateMove(board, fromX, fromY, toX, toY)\r
9645      Board board;\r
9646      int fromX;\r
9647      int fromY;\r
9648      int toX;\r
9649      int toY;\r
9650 {\r
9651   ChessSquare piece;\r
9652   POINT start, finish, mid;\r
9653   POINT frames[kFactor * 2 + 1];\r
9654   int nFrames, n;\r
9655 \r
9656   if (!appData.animate) return;\r
9657   if (doingSizing) return;\r
9658   if (fromY < 0 || fromX < 0) return;\r
9659   piece = board[fromY][fromX];\r
9660   if (piece >= EmptySquare) return;\r
9661 \r
9662   ScreenSquare(fromX, fromY, &start);\r
9663   ScreenSquare(toX, toY, &finish);\r
9664 \r
9665   /* All moves except knight jumps move in straight line */\r
9666   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9667     mid.x = start.x + (finish.x - start.x) / 2;\r
9668     mid.y = start.y + (finish.y - start.y) / 2;\r
9669   } else {\r
9670     /* Knight: make straight movement then diagonal */\r
9671     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9672        mid.x = start.x + (finish.x - start.x) / 2;\r
9673        mid.y = start.y;\r
9674      } else {\r
9675        mid.x = start.x;\r
9676        mid.y = start.y + (finish.y - start.y) / 2;\r
9677      }\r
9678   }\r
9679   \r
9680   /* Don't use as many frames for very short moves */\r
9681   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9682     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9683   else\r
9684     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9685 \r
9686   animInfo.from.x = fromX;\r
9687   animInfo.from.y = fromY;\r
9688   animInfo.to.x = toX;\r
9689   animInfo.to.y = toY;\r
9690   animInfo.lastpos = start;\r
9691   animInfo.piece = piece;\r
9692   for (n = 0; n < nFrames; n++) {\r
9693     animInfo.pos = frames[n];\r
9694     DrawPosition(FALSE, NULL);\r
9695     animInfo.lastpos = animInfo.pos;\r
9696     Sleep(appData.animSpeed);\r
9697   }\r
9698   animInfo.pos = finish;\r
9699   DrawPosition(FALSE, NULL);\r
9700   animInfo.piece = EmptySquare;\r
9701   Explode(board, fromX, fromY, toX, toY);\r
9702 }\r
9703 \r
9704 /*      Convert board position to corner of screen rect and color       */\r
9705 \r
9706 static void\r
9707 ScreenSquare(column, row, pt)\r
9708      int column; int row; POINT * pt;\r
9709 {\r
9710   if (flipView) {\r
9711     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9712     pt->y = lineGap + row * (squareSize + lineGap);\r
9713   } else {\r
9714     pt->x = lineGap + column * (squareSize + lineGap);\r
9715     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9716   }\r
9717 }\r
9718 \r
9719 /*      Generate a series of frame coords from start->mid->finish.\r
9720         The movement rate doubles until the half way point is\r
9721         reached, then halves back down to the final destination,\r
9722         which gives a nice slow in/out effect. The algorithmn\r
9723         may seem to generate too many intermediates for short\r
9724         moves, but remember that the purpose is to attract the\r
9725         viewers attention to the piece about to be moved and\r
9726         then to where it ends up. Too few frames would be less\r
9727         noticeable.                                             */\r
9728 \r
9729 static void\r
9730 Tween(start, mid, finish, factor, frames, nFrames)\r
9731      POINT * start; POINT * mid;\r
9732      POINT * finish; int factor;\r
9733      POINT frames[]; int * nFrames;\r
9734 {\r
9735   int n, fraction = 1, count = 0;\r
9736 \r
9737   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9738   for (n = 0; n < factor; n++)\r
9739     fraction *= 2;\r
9740   for (n = 0; n < factor; n++) {\r
9741     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9742     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9743     count ++;\r
9744     fraction = fraction / 2;\r
9745   }\r
9746   \r
9747   /* Midpoint */\r
9748   frames[count] = *mid;\r
9749   count ++;\r
9750   \r
9751   /* Slow out, stepping 1/2, then 1/4, ... */\r
9752   fraction = 2;\r
9753   for (n = 0; n < factor; n++) {\r
9754     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9755     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9756     count ++;\r
9757     fraction = fraction * 2;\r
9758   }\r
9759   *nFrames = count;\r
9760 }\r
9761 \r
9762 void\r
9763 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9764 {\r
9765     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9766 \r
9767     EvalGraphSet( first, last, current, pvInfoList );\r
9768 }\r
9769 \r
9770 void\r
9771 SettingsPopUp(ChessProgramState *cps)\r
9772 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9773       EngineOptionsPopup(savedHwnd, cps);\r
9774 }\r