Display error for wrong use of Machine Match
[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, OPT_VariantSChess,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][10];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
349     if((f = fopen(buf, "r")) == NULL) return;\r
350     while((k = fgetc(f)) != EOF) {\r
351         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
352         languageBuf[i] = k;\r
353         if(k == '\n') {\r
354             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
355                 char *p;\r
356                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
357                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
358                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
359                         english[j] = languageBuf + n + 1; *p = 0;\r
360                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
362                     }\r
363                 }\r
364             }\r
365             n = i + 1;\r
366         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
367             switch(k) {\r
368               case 'n': k = '\n'; break;\r
369               case 'r': k = '\r'; break;\r
370               case 't': k = '\t'; break;\r
371             }\r
372             languageBuf[--i] = k;\r
373         }\r
374         i++;\r
375     }\r
376     fclose(f);\r
377     barbaric = (j != 0);\r
378     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
379 }\r
380 \r
381 char *\r
382 T_(char *s)\r
383 {   // return the translation of the given string\r
384     // efficiency can be improved a lot...\r
385     int i=0;\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
387     if(!barbaric) return s;\r
388     if(!s) return ""; // sanity\r
389     while(english[i]) {\r
390         if(!strcmp(s, english[i])) return foreign[i];\r
391         i++;\r
392     }\r
393     return s;\r
394 }\r
395 \r
396 void\r
397 Translate(HWND hDlg, int dialogID)\r
398 {   // translate all text items in the given dialog\r
399     int i=0, j, k;\r
400     char buf[MSG_SIZ], *s;\r
401 //if(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 9\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
554   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
555   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), 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       if(gameInfo.variant == VariantSChess) {\r
2490         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2491         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2492         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2493       } else {\r
2494         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2495         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2496         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2497       }\r
2498     }\r
2499     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2500       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2501       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2502       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2503     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2504       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2505       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2506       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2507     } else { // WinBoard standard\r
2508       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2509       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2510       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2511     }\r
2512   }\r
2513 \r
2514 \r
2515   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2516     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2517     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2518     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2519     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2520     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2521     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2522     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2523     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2524     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2525     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2526     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2527     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2528     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2529     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2530     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2531     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2532     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2533     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2534     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2535     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2536     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2537     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2538     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2539     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2540     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2541     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2542     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2543     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2544     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2545     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2546 \r
2547     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2548       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2549       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2550       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2551       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2552       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2553       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2554       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2555       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2556       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2557       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2558       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2559       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2560     } else {\r
2561       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2562       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2563       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2564       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2565       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2566       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2567       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2568       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2569       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2570       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2571       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2572       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2573     }\r
2574 \r
2575   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2576     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2577     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2578     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2579     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2580     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2581     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2582     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2583     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2584     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2585     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2586     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2587     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2588     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2589     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2590   }\r
2591 \r
2592 \r
2593   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2594   /* special Shogi support in this size */\r
2595   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2596       for (piece = WhitePawn;\r
2597            (int) piece < (int) BlackPawn;\r
2598            piece = (ChessSquare) ((int) piece + 1)) {\r
2599         if (pieceBitmap[i][piece] != NULL)\r
2600           DeleteObject(pieceBitmap[i][piece]);\r
2601       }\r
2602     }\r
2603   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2604   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2605   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2606   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2607   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2608   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2609   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2610   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2611   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2612   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2613   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2614   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2615   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2616   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2617   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2618   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2619   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2620   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2621   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2622   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2623   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2624   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2625   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2626   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2627   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2628   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2629   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2630   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2631   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2632   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2633   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2634   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2635   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2636   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2637   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2638   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2639   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2640   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2641   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2642   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2643   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2644   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2645   minorSize = 0;\r
2646   }\r
2647 }\r
2648 \r
2649 HBITMAP\r
2650 PieceBitmap(ChessSquare p, int kind)\r
2651 {\r
2652   if ((int) p >= (int) BlackPawn)\r
2653     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2654 \r
2655   return pieceBitmap[kind][(int) p];\r
2656 }\r
2657 \r
2658 /***************************************************************/\r
2659 \r
2660 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2661 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2662 /*\r
2663 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2664 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2665 */\r
2666 \r
2667 VOID\r
2668 SquareToPos(int row, int column, int * x, int * y)\r
2669 {\r
2670   if (flipView) {\r
2671     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2672     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2673   } else {\r
2674     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2675     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2676   }\r
2677 }\r
2678 \r
2679 VOID\r
2680 DrawCoordsOnDC(HDC hdc)\r
2681 {\r
2682   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
2683   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
2684   char str[2] = { NULLCHAR, NULLCHAR };\r
2685   int oldMode, oldAlign, x, y, start, i;\r
2686   HFONT oldFont;\r
2687   HBRUSH oldBrush;\r
2688 \r
2689   if (!appData.showCoords)\r
2690     return;\r
2691 \r
2692   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2693 \r
2694   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2695   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2696   oldAlign = GetTextAlign(hdc);\r
2697   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2698 \r
2699   y = boardRect.top + lineGap;\r
2700   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2701 \r
2702   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2703   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2704     str[0] = files[start + i];\r
2705     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2706     y += squareSize + lineGap;\r
2707   }\r
2708 \r
2709   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2710 \r
2711   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2712   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2713     str[0] = ranks[start + i];\r
2714     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2715     x += squareSize + lineGap;\r
2716   }    \r
2717 \r
2718   SelectObject(hdc, oldBrush);\r
2719   SetBkMode(hdc, oldMode);\r
2720   SetTextAlign(hdc, oldAlign);\r
2721   SelectObject(hdc, oldFont);\r
2722 }\r
2723 \r
2724 VOID\r
2725 DrawGridOnDC(HDC hdc)\r
2726 {\r
2727   HPEN oldPen;\r
2728  \r
2729   if (lineGap != 0) {\r
2730     oldPen = SelectObject(hdc, gridPen);\r
2731     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2732     SelectObject(hdc, oldPen);\r
2733   }\r
2734 }\r
2735 \r
2736 #define HIGHLIGHT_PEN 0\r
2737 #define PREMOVE_PEN   1\r
2738 \r
2739 VOID\r
2740 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2741 {\r
2742   int x1, y1;\r
2743   HPEN oldPen, hPen;\r
2744   if (lineGap == 0) return;\r
2745   if (flipView) {\r
2746     x1 = boardRect.left +\r
2747       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2748     y1 = boardRect.top +\r
2749       lineGap/2 + y * (squareSize + lineGap);\r
2750   } else {\r
2751     x1 = boardRect.left +\r
2752       lineGap/2 + x * (squareSize + lineGap);\r
2753     y1 = boardRect.top +\r
2754       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2755   }\r
2756   hPen = pen ? premovePen : highlightPen;\r
2757   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2758   MoveToEx(hdc, x1, y1, NULL);\r
2759   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2760   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2761   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2762   LineTo(hdc, x1, y1);\r
2763   SelectObject(hdc, oldPen);\r
2764 }\r
2765 \r
2766 VOID\r
2767 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2768 {\r
2769   int i;\r
2770   for (i=0; i<2; i++) {\r
2771     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2772       DrawHighlightOnDC(hdc, TRUE,\r
2773                         h->sq[i].x, h->sq[i].y,\r
2774                         pen);\r
2775   }\r
2776 }\r
2777 \r
2778 /* Note: sqcolor is used only in monoMode */\r
2779 /* Note that this code is largely duplicated in woptions.c,\r
2780    function DrawSampleSquare, so that needs to be updated too */\r
2781 VOID\r
2782 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2783 {\r
2784   HBITMAP oldBitmap;\r
2785   HBRUSH oldBrush;\r
2786   int tmpSize;\r
2787 \r
2788   if (appData.blindfold) return;\r
2789 \r
2790   /* [AS] Use font-based pieces if needed */\r
2791   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2792     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2793     CreatePiecesFromFont();\r
2794 \r
2795     if( fontBitmapSquareSize == squareSize ) {\r
2796         int index = TranslatePieceToFontPiece(piece);\r
2797 \r
2798         SelectObject( tmphdc, hPieceMask[ index ] );\r
2799 \r
2800       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2801         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2802       else\r
2803         BitBlt( hdc,\r
2804             x, y,\r
2805             squareSize, squareSize,\r
2806             tmphdc,\r
2807             0, 0,\r
2808             SRCAND );\r
2809 \r
2810         SelectObject( tmphdc, hPieceFace[ index ] );\r
2811 \r
2812       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2813         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2814       else\r
2815         BitBlt( hdc,\r
2816             x, y,\r
2817             squareSize, squareSize,\r
2818             tmphdc,\r
2819             0, 0,\r
2820             SRCPAINT );\r
2821 \r
2822         return;\r
2823     }\r
2824   }\r
2825 \r
2826   if (appData.monoMode) {\r
2827     SelectObject(tmphdc, PieceBitmap(piece, \r
2828       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2829     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2830            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2831   } else {\r
2832     tmpSize = squareSize;\r
2833     if(minorSize &&\r
2834         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2835          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2836       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2837       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2838       x += (squareSize - minorSize)>>1;\r
2839       y += squareSize - minorSize - 2;\r
2840       tmpSize = minorSize;\r
2841     }\r
2842     if (color || appData.allWhite ) {\r
2843       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2844       if( color )\r
2845               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2846       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2847       if(appData.upsideDown && color==flipView)\r
2848         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2849       else\r
2850         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2851       /* Use black for outline of white pieces */\r
2852       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2853       if(appData.upsideDown && color==flipView)\r
2854         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2855       else\r
2856         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2857     } else {\r
2858       /* Use square color for details of black pieces */\r
2859       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2860       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2861       if(appData.upsideDown && !flipView)\r
2862         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2863       else\r
2864         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2865     }\r
2866     SelectObject(hdc, oldBrush);\r
2867     SelectObject(tmphdc, oldBitmap);\r
2868   }\r
2869 }\r
2870 \r
2871 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2872 int GetBackTextureMode( int algo )\r
2873 {\r
2874     int result = BACK_TEXTURE_MODE_DISABLED;\r
2875 \r
2876     switch( algo ) \r
2877     {\r
2878         case BACK_TEXTURE_MODE_PLAIN:\r
2879             result = 1; /* Always use identity map */\r
2880             break;\r
2881         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2882             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2883             break;\r
2884     }\r
2885 \r
2886     return result;\r
2887 }\r
2888 \r
2889 /* \r
2890     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2891     to handle redraws cleanly (as random numbers would always be different).\r
2892 */\r
2893 VOID RebuildTextureSquareInfo()\r
2894 {\r
2895     BITMAP bi;\r
2896     int lite_w = 0;\r
2897     int lite_h = 0;\r
2898     int dark_w = 0;\r
2899     int dark_h = 0;\r
2900     int row;\r
2901     int col;\r
2902 \r
2903     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2904 \r
2905     if( liteBackTexture != NULL ) {\r
2906         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2907             lite_w = bi.bmWidth;\r
2908             lite_h = bi.bmHeight;\r
2909         }\r
2910     }\r
2911 \r
2912     if( darkBackTexture != NULL ) {\r
2913         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2914             dark_w = bi.bmWidth;\r
2915             dark_h = bi.bmHeight;\r
2916         }\r
2917     }\r
2918 \r
2919     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2920         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2921             if( (col + row) & 1 ) {\r
2922                 /* Lite square */\r
2923                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2924                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2925                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2926                   else\r
2927                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2928                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2929                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2930                   else\r
2931                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2932                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2933                 }\r
2934             }\r
2935             else {\r
2936                 /* Dark square */\r
2937                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2938                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2939                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2940                   else\r
2941                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2942                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2943                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2944                   else\r
2945                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2946                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2947                 }\r
2948             }\r
2949         }\r
2950     }\r
2951 }\r
2952 \r
2953 /* [AS] Arrow highlighting support */\r
2954 \r
2955 static int A_WIDTH = 5; /* Width of arrow body */\r
2956 \r
2957 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2958 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2959 \r
2960 static double Sqr( double x )\r
2961 {\r
2962     return x*x;\r
2963 }\r
2964 \r
2965 static int Round( double x )\r
2966 {\r
2967     return (int) (x + 0.5);\r
2968 }\r
2969 \r
2970 /* Draw an arrow between two points using current settings */\r
2971 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2972 {\r
2973     POINT arrow[7];\r
2974     double dx, dy, j, k, x, y;\r
2975 \r
2976     if( d_x == s_x ) {\r
2977         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2978 \r
2979         arrow[0].x = s_x + A_WIDTH;\r
2980         arrow[0].y = s_y;\r
2981 \r
2982         arrow[1].x = s_x + A_WIDTH;\r
2983         arrow[1].y = d_y - h;\r
2984 \r
2985         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2986         arrow[2].y = d_y - h;\r
2987 \r
2988         arrow[3].x = d_x;\r
2989         arrow[3].y = d_y;\r
2990 \r
2991         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2992         arrow[4].y = d_y - h;\r
2993 \r
2994         arrow[5].x = s_x - A_WIDTH;\r
2995         arrow[5].y = d_y - h;\r
2996 \r
2997         arrow[6].x = s_x - A_WIDTH;\r
2998         arrow[6].y = s_y;\r
2999     }\r
3000     else if( d_y == s_y ) {\r
3001         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3002 \r
3003         arrow[0].x = s_x;\r
3004         arrow[0].y = s_y + A_WIDTH;\r
3005 \r
3006         arrow[1].x = d_x - w;\r
3007         arrow[1].y = s_y + A_WIDTH;\r
3008 \r
3009         arrow[2].x = d_x - w;\r
3010         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3011 \r
3012         arrow[3].x = d_x;\r
3013         arrow[3].y = d_y;\r
3014 \r
3015         arrow[4].x = d_x - w;\r
3016         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3017 \r
3018         arrow[5].x = d_x - w;\r
3019         arrow[5].y = s_y - A_WIDTH;\r
3020 \r
3021         arrow[6].x = s_x;\r
3022         arrow[6].y = s_y - A_WIDTH;\r
3023     }\r
3024     else {\r
3025         /* [AS] Needed a lot of paper for this! :-) */\r
3026         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3027         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3028   \r
3029         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3030 \r
3031         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3032 \r
3033         x = s_x;\r
3034         y = s_y;\r
3035 \r
3036         arrow[0].x = Round(x - j);\r
3037         arrow[0].y = Round(y + j*dx);\r
3038 \r
3039         arrow[1].x = Round(x + j);\r
3040         arrow[1].y = Round(y - j*dx);\r
3041 \r
3042         if( d_x > s_x ) {\r
3043             x = (double) d_x - k;\r
3044             y = (double) d_y - k*dy;\r
3045         }\r
3046         else {\r
3047             x = (double) d_x + k;\r
3048             y = (double) d_y + k*dy;\r
3049         }\r
3050 \r
3051         arrow[2].x = Round(x + j);\r
3052         arrow[2].y = Round(y - j*dx);\r
3053 \r
3054         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3055         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3056 \r
3057         arrow[4].x = d_x;\r
3058         arrow[4].y = d_y;\r
3059 \r
3060         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3061         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3062 \r
3063         arrow[6].x = Round(x - j);\r
3064         arrow[6].y = Round(y + j*dx);\r
3065     }\r
3066 \r
3067     Polygon( hdc, arrow, 7 );\r
3068 }\r
3069 \r
3070 /* [AS] Draw an arrow between two squares */\r
3071 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3072 {\r
3073     int s_x, s_y, d_x, d_y;\r
3074     HPEN hpen;\r
3075     HPEN holdpen;\r
3076     HBRUSH hbrush;\r
3077     HBRUSH holdbrush;\r
3078     LOGBRUSH stLB;\r
3079 \r
3080     if( s_col == d_col && s_row == d_row ) {\r
3081         return;\r
3082     }\r
3083 \r
3084     /* Get source and destination points */\r
3085     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3086     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3087 \r
3088     if( d_y > s_y ) {\r
3089         d_y += squareSize / 4;\r
3090     }\r
3091     else if( d_y < s_y ) {\r
3092         d_y += 3 * squareSize / 4;\r
3093     }\r
3094     else {\r
3095         d_y += squareSize / 2;\r
3096     }\r
3097 \r
3098     if( d_x > s_x ) {\r
3099         d_x += squareSize / 4;\r
3100     }\r
3101     else if( d_x < s_x ) {\r
3102         d_x += 3 * squareSize / 4;\r
3103     }\r
3104     else {\r
3105         d_x += squareSize / 2;\r
3106     }\r
3107 \r
3108     s_x += squareSize / 2;\r
3109     s_y += squareSize / 2;\r
3110 \r
3111     /* Adjust width */\r
3112     A_WIDTH = squareSize / 14;\r
3113 \r
3114     /* Draw */\r
3115     stLB.lbStyle = BS_SOLID;\r
3116     stLB.lbColor = appData.highlightArrowColor;\r
3117     stLB.lbHatch = 0;\r
3118 \r
3119     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3120     holdpen = SelectObject( hdc, hpen );\r
3121     hbrush = CreateBrushIndirect( &stLB );\r
3122     holdbrush = SelectObject( hdc, hbrush );\r
3123 \r
3124     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3125 \r
3126     SelectObject( hdc, holdpen );\r
3127     SelectObject( hdc, holdbrush );\r
3128     DeleteObject( hpen );\r
3129     DeleteObject( hbrush );\r
3130 }\r
3131 \r
3132 BOOL HasHighlightInfo()\r
3133 {\r
3134     BOOL result = FALSE;\r
3135 \r
3136     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3137         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3138     {\r
3139         result = TRUE;\r
3140     }\r
3141 \r
3142     return result;\r
3143 }\r
3144 \r
3145 BOOL IsDrawArrowEnabled()\r
3146 {\r
3147     BOOL result = FALSE;\r
3148 \r
3149     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3150         result = TRUE;\r
3151     }\r
3152 \r
3153     return result;\r
3154 }\r
3155 \r
3156 VOID DrawArrowHighlight( HDC hdc )\r
3157 {\r
3158     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3159         DrawArrowBetweenSquares( hdc,\r
3160             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3161             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3162     }\r
3163 }\r
3164 \r
3165 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3166 {\r
3167     HRGN result = NULL;\r
3168 \r
3169     if( HasHighlightInfo() ) {\r
3170         int x1, y1, x2, y2;\r
3171         int sx, sy, dx, dy;\r
3172 \r
3173         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3174         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3175 \r
3176         sx = MIN( x1, x2 );\r
3177         sy = MIN( y1, y2 );\r
3178         dx = MAX( x1, x2 ) + squareSize;\r
3179         dy = MAX( y1, y2 ) + squareSize;\r
3180 \r
3181         result = CreateRectRgn( sx, sy, dx, dy );\r
3182     }\r
3183 \r
3184     return result;\r
3185 }\r
3186 \r
3187 /*\r
3188     Warning: this function modifies the behavior of several other functions. \r
3189     \r
3190     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3191     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3192     repaint is scattered all over the place, which is not good for features such as\r
3193     "arrow highlighting" that require a full repaint of the board.\r
3194 \r
3195     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3196     user interaction, when speed is not so important) but especially to avoid errors\r
3197     in the displayed graphics.\r
3198 \r
3199     In such patched places, I always try refer to this function so there is a single\r
3200     place to maintain knowledge.\r
3201     \r
3202     To restore the original behavior, just return FALSE unconditionally.\r
3203 */\r
3204 BOOL IsFullRepaintPreferrable()\r
3205 {\r
3206     BOOL result = FALSE;\r
3207 \r
3208     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3209         /* Arrow may appear on the board */\r
3210         result = TRUE;\r
3211     }\r
3212 \r
3213     return result;\r
3214 }\r
3215 \r
3216 /* \r
3217     This function is called by DrawPosition to know whether a full repaint must\r
3218     be forced or not.\r
3219 \r
3220     Only DrawPosition may directly call this function, which makes use of \r
3221     some state information. Other function should call DrawPosition specifying \r
3222     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3223 */\r
3224 BOOL DrawPositionNeedsFullRepaint()\r
3225 {\r
3226     BOOL result = FALSE;\r
3227 \r
3228     /* \r
3229         Probably a slightly better policy would be to trigger a full repaint\r
3230         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3231         but animation is fast enough that it's difficult to notice.\r
3232     */\r
3233     if( animInfo.piece == EmptySquare ) {\r
3234         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3235             result = TRUE;\r
3236         }\r
3237     }\r
3238 \r
3239     return result;\r
3240 }\r
3241 \r
3242 VOID\r
3243 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3244 {\r
3245   int row, column, x, y, square_color, piece_color;\r
3246   ChessSquare piece;\r
3247   HBRUSH oldBrush;\r
3248   HDC texture_hdc = NULL;\r
3249 \r
3250   /* [AS] Initialize background textures if needed */\r
3251   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3252       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3253       if( backTextureSquareSize != squareSize \r
3254        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3255           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3256           backTextureSquareSize = squareSize;\r
3257           RebuildTextureSquareInfo();\r
3258       }\r
3259 \r
3260       texture_hdc = CreateCompatibleDC( hdc );\r
3261   }\r
3262 \r
3263   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3264     for (column = 0; column < BOARD_WIDTH; column++) {\r
3265   \r
3266       SquareToPos(row, column, &x, &y);\r
3267 \r
3268       piece = board[row][column];\r
3269 \r
3270       square_color = ((column + row) % 2) == 1;\r
3271       if( gameInfo.variant == VariantXiangqi ) {\r
3272           square_color = !InPalace(row, column);\r
3273           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3274           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3275       }\r
3276       piece_color = (int) piece < (int) BlackPawn;\r
3277 \r
3278 \r
3279       /* [HGM] holdings file: light square or black */\r
3280       if(column == BOARD_LEFT-2) {\r
3281             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3282                 square_color = 1;\r
3283             else {\r
3284                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3285                 continue;\r
3286             }\r
3287       } else\r
3288       if(column == BOARD_RGHT + 1 ) {\r
3289             if( row < gameInfo.holdingsSize )\r
3290                 square_color = 1;\r
3291             else {\r
3292                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3293                 continue;\r
3294             }\r
3295       }\r
3296       if(column == BOARD_LEFT-1 ) /* left align */\r
3297             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3298       else if( column == BOARD_RGHT) /* right align */\r
3299             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3300       else\r
3301       if (appData.monoMode) {\r
3302         if (piece == EmptySquare) {\r
3303           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3304                  square_color ? WHITENESS : BLACKNESS);\r
3305         } else {\r
3306           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3307         }\r
3308       } \r
3309       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3310           /* [AS] Draw the square using a texture bitmap */\r
3311           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3312           int r = row, c = column; // [HGM] do not flip board in flipView\r
3313           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3314 \r
3315           DrawTile( x, y, \r
3316               squareSize, squareSize, \r
3317               hdc, \r
3318               texture_hdc,\r
3319               backTextureSquareInfo[r][c].mode,\r
3320               backTextureSquareInfo[r][c].x,\r
3321               backTextureSquareInfo[r][c].y );\r
3322 \r
3323           SelectObject( texture_hdc, hbm );\r
3324 \r
3325           if (piece != EmptySquare) {\r
3326               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3327           }\r
3328       }\r
3329       else {\r
3330         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3331 \r
3332         oldBrush = SelectObject(hdc, brush );\r
3333         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3334         SelectObject(hdc, oldBrush);\r
3335         if (piece != EmptySquare)\r
3336           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3337       }\r
3338     }\r
3339   }\r
3340 \r
3341   if( texture_hdc != NULL ) {\r
3342     DeleteDC( texture_hdc );\r
3343   }\r
3344 }\r
3345 \r
3346 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3347 void fputDW(FILE *f, int x)\r
3348 {\r
3349         fputc(x     & 255, f);\r
3350         fputc(x>>8  & 255, f);\r
3351         fputc(x>>16 & 255, f);\r
3352         fputc(x>>24 & 255, f);\r
3353 }\r
3354 \r
3355 #define MAX_CLIPS 200   /* more than enough */\r
3356 \r
3357 VOID\r
3358 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3359 {\r
3360 //  HBITMAP bufferBitmap;\r
3361   BITMAP bi;\r
3362 //  RECT Rect;\r
3363   HDC tmphdc;\r
3364   HBITMAP hbm;\r
3365   int w = 100, h = 50;\r
3366 \r
3367   if(logo == NULL) return;\r
3368 //  GetClientRect(hwndMain, &Rect);\r
3369 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3370 //                                      Rect.bottom-Rect.top+1);\r
3371   tmphdc = CreateCompatibleDC(hdc);\r
3372   hbm = SelectObject(tmphdc, logo);\r
3373   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3374             w = bi.bmWidth;\r
3375             h = bi.bmHeight;\r
3376   }\r
3377   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3378                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3379   SelectObject(tmphdc, hbm);\r
3380   DeleteDC(tmphdc);\r
3381 }\r
3382 \r
3383 static HDC hdcSeek;\r
3384 \r
3385 // [HGM] seekgraph\r
3386 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3387 {\r
3388     POINT stPt;\r
3389     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3390     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3391     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3392     SelectObject( hdcSeek, hp );\r
3393 }\r
3394 \r
3395 // front-end wrapper for drawing functions to do rectangles\r
3396 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3397 {\r
3398     HPEN hp;\r
3399     RECT rc;\r
3400 \r
3401     if (hdcSeek == NULL) {\r
3402     hdcSeek = GetDC(hwndMain);\r
3403       if (!appData.monoMode) {\r
3404         SelectPalette(hdcSeek, hPal, FALSE);\r
3405         RealizePalette(hdcSeek);\r
3406       }\r
3407     }\r
3408     hp = SelectObject( hdcSeek, gridPen );\r
3409     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3410     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3411     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3412     SelectObject( hdcSeek, hp );\r
3413 }\r
3414 \r
3415 // front-end wrapper for putting text in graph\r
3416 void DrawSeekText(char *buf, int x, int y)\r
3417 {\r
3418         SIZE stSize;\r
3419         SetBkMode( hdcSeek, TRANSPARENT );\r
3420         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3421         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3422 }\r
3423 \r
3424 void DrawSeekDot(int x, int y, int color)\r
3425 {\r
3426         int square = color & 0x80;\r
3427         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3428                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3429         color &= 0x7F;\r
3430         if(square)\r
3431             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3432                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3433         else\r
3434             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3435                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3436             SelectObject(hdcSeek, oldBrush);\r
3437 }\r
3438 \r
3439 VOID\r
3440 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3441 {\r
3442   static Board lastReq[2], lastDrawn[2];\r
3443   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3444   static int lastDrawnFlipView = 0;\r
3445   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3446   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3447   HDC tmphdc;\r
3448   HDC hdcmem;\r
3449   HBITMAP bufferBitmap;\r
3450   HBITMAP oldBitmap;\r
3451   RECT Rect;\r
3452   HRGN clips[MAX_CLIPS];\r
3453   ChessSquare dragged_piece = EmptySquare;\r
3454   int nr = twoBoards*partnerUp;\r
3455 \r
3456   /* I'm undecided on this - this function figures out whether a full\r
3457    * repaint is necessary on its own, so there's no real reason to have the\r
3458    * caller tell it that.  I think this can safely be set to FALSE - but\r
3459    * if we trust the callers not to request full repaints unnessesarily, then\r
3460    * we could skip some clipping work.  In other words, only request a full\r
3461    * redraw when the majority of pieces have changed positions (ie. flip, \r
3462    * gamestart and similar)  --Hawk\r
3463    */\r
3464   Boolean fullrepaint = repaint;\r
3465 \r
3466   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3467 \r
3468   if( DrawPositionNeedsFullRepaint() ) {\r
3469       fullrepaint = TRUE;\r
3470   }\r
3471 \r
3472   if (board == NULL) {\r
3473     if (!lastReqValid[nr]) {\r
3474       return;\r
3475     }\r
3476     board = lastReq[nr];\r
3477   } else {\r
3478     CopyBoard(lastReq[nr], board);\r
3479     lastReqValid[nr] = 1;\r
3480   }\r
3481 \r
3482   if (doingSizing) {\r
3483     return;\r
3484   }\r
3485 \r
3486   if (IsIconic(hwndMain)) {\r
3487     return;\r
3488   }\r
3489 \r
3490   if (hdc == NULL) {\r
3491     hdc = GetDC(hwndMain);\r
3492     if (!appData.monoMode) {\r
3493       SelectPalette(hdc, hPal, FALSE);\r
3494       RealizePalette(hdc);\r
3495     }\r
3496     releaseDC = TRUE;\r
3497   } else {\r
3498     releaseDC = FALSE;\r
3499   }\r
3500 \r
3501   /* Create some work-DCs */\r
3502   hdcmem = CreateCompatibleDC(hdc);\r
3503   tmphdc = CreateCompatibleDC(hdc);\r
3504 \r
3505   /* If dragging is in progress, we temporarely remove the piece */\r
3506   /* [HGM] or temporarily decrease count if stacked              */\r
3507   /*       !! Moved to before board compare !!                   */\r
3508   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3509     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3510     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3511             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3512         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3513     } else \r
3514     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3515             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3516         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3517     } else \r
3518         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3519   }\r
3520 \r
3521   /* Figure out which squares need updating by comparing the \r
3522    * newest board with the last drawn board and checking if\r
3523    * flipping has changed.\r
3524    */\r
3525   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3526     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3527       for (column = 0; column < BOARD_WIDTH; column++) {\r
3528         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3529           SquareToPos(row, column, &x, &y);\r
3530           clips[num_clips++] =\r
3531             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3532         }\r
3533       }\r
3534     }\r
3535    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3536     for (i=0; i<2; i++) {\r
3537       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3538           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3539         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3540             lastDrawnHighlight.sq[i].y >= 0) {\r
3541           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3542                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3543           clips[num_clips++] =\r
3544             CreateRectRgn(x - lineGap, y - lineGap, \r
3545                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3546         }\r
3547         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3548           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3549           clips[num_clips++] =\r
3550             CreateRectRgn(x - lineGap, y - lineGap, \r
3551                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3552         }\r
3553       }\r
3554     }\r
3555     for (i=0; i<2; i++) {\r
3556       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3557           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3558         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3559             lastDrawnPremove.sq[i].y >= 0) {\r
3560           SquareToPos(lastDrawnPremove.sq[i].y,\r
3561                       lastDrawnPremove.sq[i].x, &x, &y);\r
3562           clips[num_clips++] =\r
3563             CreateRectRgn(x - lineGap, y - lineGap, \r
3564                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3565         }\r
3566         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3567             premoveHighlightInfo.sq[i].y >= 0) {\r
3568           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3569                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3570           clips[num_clips++] =\r
3571             CreateRectRgn(x - lineGap, y - lineGap, \r
3572                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3573         }\r
3574       }\r
3575     }\r
3576    } else { // nr == 1\r
3577         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3578         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3579         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3580         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3581       for (i=0; i<2; i++) {\r
3582         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3583             partnerHighlightInfo.sq[i].y >= 0) {\r
3584           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3585                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3586           clips[num_clips++] =\r
3587             CreateRectRgn(x - lineGap, y - lineGap, \r
3588                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3589         }\r
3590         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3591             oldPartnerHighlight.sq[i].y >= 0) {\r
3592           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3593                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3594           clips[num_clips++] =\r
3595             CreateRectRgn(x - lineGap, y - lineGap, \r
3596                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3597         }\r
3598       }\r
3599    }\r
3600   } else {\r
3601     fullrepaint = TRUE;\r
3602   }\r
3603 \r
3604   /* Create a buffer bitmap - this is the actual bitmap\r
3605    * being written to.  When all the work is done, we can\r
3606    * copy it to the real DC (the screen).  This avoids\r
3607    * the problems with flickering.\r
3608    */\r
3609   GetClientRect(hwndMain, &Rect);\r
3610   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3611                                         Rect.bottom-Rect.top+1);\r
3612   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3613   if (!appData.monoMode) {\r
3614     SelectPalette(hdcmem, hPal, FALSE);\r
3615   }\r
3616 \r
3617   /* Create clips for dragging */\r
3618   if (!fullrepaint) {\r
3619     if (dragInfo.from.x >= 0) {\r
3620       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3621       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3622     }\r
3623     if (dragInfo.start.x >= 0) {\r
3624       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3625       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3626     }\r
3627     if (dragInfo.pos.x >= 0) {\r
3628       x = dragInfo.pos.x - squareSize / 2;\r
3629       y = dragInfo.pos.y - squareSize / 2;\r
3630       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3631     }\r
3632     if (dragInfo.lastpos.x >= 0) {\r
3633       x = dragInfo.lastpos.x - squareSize / 2;\r
3634       y = dragInfo.lastpos.y - squareSize / 2;\r
3635       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3636     }\r
3637   }\r
3638 \r
3639   /* Are we animating a move?  \r
3640    * If so, \r
3641    *   - remove the piece from the board (temporarely)\r
3642    *   - calculate the clipping region\r
3643    */\r
3644   if (!fullrepaint) {\r
3645     if (animInfo.piece != EmptySquare) {\r
3646       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3647       x = boardRect.left + animInfo.lastpos.x;\r
3648       y = boardRect.top + animInfo.lastpos.y;\r
3649       x2 = boardRect.left + animInfo.pos.x;\r
3650       y2 = boardRect.top + animInfo.pos.y;\r
3651       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3652       /* Slight kludge.  The real problem is that after AnimateMove is\r
3653          done, the position on the screen does not match lastDrawn.\r
3654          This currently causes trouble only on e.p. captures in\r
3655          atomic, where the piece moves to an empty square and then\r
3656          explodes.  The old and new positions both had an empty square\r
3657          at the destination, but animation has drawn a piece there and\r
3658          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3659       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3660     }\r
3661   }\r
3662 \r
3663   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3664   if (num_clips == 0)\r
3665     fullrepaint = TRUE;\r
3666 \r
3667   /* Set clipping on the memory DC */\r
3668   if (!fullrepaint) {\r
3669     SelectClipRgn(hdcmem, clips[0]);\r
3670     for (x = 1; x < num_clips; x++) {\r
3671       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3672         abort();  // this should never ever happen!\r
3673     }\r
3674   }\r
3675 \r
3676   /* Do all the drawing to the memory DC */\r
3677   if(explodeInfo.radius) { // [HGM] atomic\r
3678         HBRUSH oldBrush;\r
3679         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3680         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3681         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3682         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3683         x += squareSize/2;\r
3684         y += squareSize/2;\r
3685         if(!fullrepaint) {\r
3686           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3687           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3688         }\r
3689         DrawGridOnDC(hdcmem);\r
3690         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3691         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3692         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3693         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3694         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3695         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3696         SelectObject(hdcmem, oldBrush);\r
3697   } else {\r
3698     DrawGridOnDC(hdcmem);\r
3699     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3700         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3701         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3702     } else {\r
3703         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3704         oldPartnerHighlight = partnerHighlightInfo;\r
3705     }\r
3706     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3707   }\r
3708   if(nr == 0) // [HGM] dual: markers only on left board\r
3709   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3710     for (column = 0; column < BOARD_WIDTH; column++) {\r
3711         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3712             HBRUSH oldBrush = SelectObject(hdcmem, \r
3713                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3714             SquareToPos(row, column, &x, &y);\r
3715             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3716                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3717             SelectObject(hdcmem, oldBrush);\r
3718         }\r
3719     }\r
3720   }\r
3721   if(logoHeight) {\r
3722         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3723         if(appData.autoLogo) {\r
3724           \r
3725           switch(gameMode) { // pick logos based on game mode\r
3726             case IcsObserving:\r
3727                 whiteLogo = second.programLogo; // ICS logo\r
3728                 blackLogo = second.programLogo;\r
3729             default:\r
3730                 break;\r
3731             case IcsPlayingWhite:\r
3732                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3733                 blackLogo = second.programLogo; // ICS logo\r
3734                 break;\r
3735             case IcsPlayingBlack:\r
3736                 whiteLogo = second.programLogo; // ICS logo\r
3737                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3738                 break;\r
3739             case TwoMachinesPlay:\r
3740                 if(first.twoMachinesColor[0] == 'b') {\r
3741                     whiteLogo = second.programLogo;\r
3742                     blackLogo = first.programLogo;\r
3743                 }\r
3744                 break;\r
3745             case MachinePlaysWhite:\r
3746                 blackLogo = userLogo;\r
3747                 break;\r
3748             case MachinePlaysBlack:\r
3749                 whiteLogo = userLogo;\r
3750                 blackLogo = first.programLogo;\r
3751           }\r
3752         }\r
3753         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3754         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3755   }\r
3756 \r
3757   if( appData.highlightMoveWithArrow ) {\r
3758     DrawArrowHighlight(hdcmem);\r
3759   }\r
3760 \r
3761   DrawCoordsOnDC(hdcmem);\r
3762 \r
3763   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3764                  /* to make sure lastDrawn contains what is actually drawn */\r
3765 \r
3766   /* Put the dragged piece back into place and draw it (out of place!) */\r
3767     if (dragged_piece != EmptySquare) {\r
3768     /* [HGM] or restack */\r
3769     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3770                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3771     else\r
3772     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3773                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3774     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3775     x = dragInfo.pos.x - squareSize / 2;\r
3776     y = dragInfo.pos.y - squareSize / 2;\r
3777     DrawPieceOnDC(hdcmem, dragged_piece,\r
3778                   ((int) dragged_piece < (int) BlackPawn), \r
3779                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3780   }   \r
3781   \r
3782   /* Put the animated piece back into place and draw it */\r
3783   if (animInfo.piece != EmptySquare) {\r
3784     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3785     x = boardRect.left + animInfo.pos.x;\r
3786     y = boardRect.top + animInfo.pos.y;\r
3787     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3788                   ((int) animInfo.piece < (int) BlackPawn),\r
3789                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3790   }\r
3791 \r
3792   /* Release the bufferBitmap by selecting in the old bitmap \r
3793    * and delete the memory DC\r
3794    */\r
3795   SelectObject(hdcmem, oldBitmap);\r
3796   DeleteDC(hdcmem);\r
3797 \r
3798   /* Set clipping on the target DC */\r
3799   if (!fullrepaint) {\r
3800     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3801         RECT rect;\r
3802         GetRgnBox(clips[x], &rect);\r
3803         DeleteObject(clips[x]);\r
3804         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3805                           rect.right + wpMain.width/2, rect.bottom);\r
3806     }\r
3807     SelectClipRgn(hdc, clips[0]);\r
3808     for (x = 1; x < num_clips; x++) {\r
3809       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3810         abort();   // this should never ever happen!\r
3811     } \r
3812   }\r
3813 \r
3814   /* Copy the new bitmap onto the screen in one go.\r
3815    * This way we avoid any flickering\r
3816    */\r
3817   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3818   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3819          boardRect.right - boardRect.left,\r
3820          boardRect.bottom - boardRect.top,\r
3821          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3822   if(saveDiagFlag) { \r
3823     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3824     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3825 \r
3826     GetObject(bufferBitmap, sizeof(b), &b);\r
3827     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3828         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3829         bih.biWidth = b.bmWidth;\r
3830         bih.biHeight = b.bmHeight;\r
3831         bih.biPlanes = 1;\r
3832         bih.biBitCount = b.bmBitsPixel;\r
3833         bih.biCompression = 0;\r
3834         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3835         bih.biXPelsPerMeter = 0;\r
3836         bih.biYPelsPerMeter = 0;\r
3837         bih.biClrUsed = 0;\r
3838         bih.biClrImportant = 0;\r
3839 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3840 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3841         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3842 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3843 \r
3844         wb = b.bmWidthBytes;\r
3845         // count colors\r
3846         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3847                 int k = ((int*) pData)[i];\r
3848                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3849                 if(j >= 16) break;\r
3850                 color[j] = k;\r
3851                 if(j >= nrColors) nrColors = j+1;\r
3852         }\r
3853         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3854                 INT p = 0;\r
3855                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3856                     for(w=0; w<(wb>>2); w+=2) {\r
3857                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3858                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3859                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3860                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3861                         pData[p++] = m | j<<4;\r
3862                     }\r
3863                     while(p&3) pData[p++] = 0;\r
3864                 }\r
3865                 fac = 3;\r
3866                 wb = ((wb+31)>>5)<<2;\r
3867         }\r
3868         // write BITMAPFILEHEADER\r
3869         fprintf(diagFile, "BM");\r
3870         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3871         fputDW(diagFile, 0);\r
3872         fputDW(diagFile, 0x36 + (fac?64:0));\r
3873         // write BITMAPINFOHEADER\r
3874         fputDW(diagFile, 40);\r
3875         fputDW(diagFile, b.bmWidth);\r
3876         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3877         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3878         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3879         fputDW(diagFile, 0);\r
3880         fputDW(diagFile, 0);\r
3881         fputDW(diagFile, 0);\r
3882         fputDW(diagFile, 0);\r
3883         fputDW(diagFile, 0);\r
3884         fputDW(diagFile, 0);\r
3885         // write color table\r
3886         if(fac)\r
3887         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3888         // write bitmap data\r
3889         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3890                 fputc(pData[i], diagFile);\r
3891      }\r
3892   }\r
3893 \r
3894   SelectObject(tmphdc, oldBitmap);\r
3895 \r
3896   /* Massive cleanup */\r
3897   for (x = 0; x < num_clips; x++)\r
3898     DeleteObject(clips[x]);\r
3899 \r
3900   DeleteDC(tmphdc);\r
3901   DeleteObject(bufferBitmap);\r
3902 \r
3903   if (releaseDC) \r
3904     ReleaseDC(hwndMain, hdc);\r
3905   \r
3906   if (lastDrawnFlipView != flipView && nr == 0) {\r
3907     if (flipView)\r
3908       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3909     else\r
3910       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3911   }\r
3912 \r
3913 /*  CopyBoard(lastDrawn, board);*/\r
3914   lastDrawnHighlight = highlightInfo;\r
3915   lastDrawnPremove   = premoveHighlightInfo;\r
3916   lastDrawnFlipView = flipView;\r
3917   lastDrawnValid[nr] = 1;\r
3918 }\r
3919 \r
3920 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3921 int\r
3922 SaveDiagram(f)\r
3923      FILE *f;\r
3924 {\r
3925     saveDiagFlag = 1; diagFile = f;\r
3926     HDCDrawPosition(NULL, TRUE, NULL);\r
3927 \r
3928     saveDiagFlag = 0;\r
3929 \r
3930 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3931     \r
3932     fclose(f);\r
3933     return TRUE;\r
3934 }\r
3935 \r
3936 \r
3937 /*---------------------------------------------------------------------------*\\r
3938 | CLIENT PAINT PROCEDURE\r
3939 |   This is the main event-handler for the WM_PAINT message.\r
3940 |\r
3941 \*---------------------------------------------------------------------------*/\r
3942 VOID\r
3943 PaintProc(HWND hwnd)\r
3944 {\r
3945   HDC         hdc;\r
3946   PAINTSTRUCT ps;\r
3947   HFONT       oldFont;\r
3948 \r
3949   if((hdc = BeginPaint(hwnd, &ps))) {\r
3950     if (IsIconic(hwnd)) {\r
3951       DrawIcon(hdc, 2, 2, iconCurrent);\r
3952     } else {\r
3953       if (!appData.monoMode) {\r
3954         SelectPalette(hdc, hPal, FALSE);\r
3955         RealizePalette(hdc);\r
3956       }\r
3957       HDCDrawPosition(hdc, 1, NULL);\r
3958       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3959         flipView = !flipView; partnerUp = !partnerUp;\r
3960         HDCDrawPosition(hdc, 1, NULL);\r
3961         flipView = !flipView; partnerUp = !partnerUp;\r
3962       }\r
3963       oldFont =\r
3964         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3965       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3966                  ETO_CLIPPED|ETO_OPAQUE,\r
3967                  &messageRect, messageText, strlen(messageText), NULL);\r
3968       SelectObject(hdc, oldFont);\r
3969       DisplayBothClocks();\r
3970     }\r
3971     EndPaint(hwnd,&ps);\r
3972   }\r
3973 \r
3974   return;\r
3975 }\r
3976 \r
3977 \r
3978 /*\r
3979  * If the user selects on a border boundary, return -1; if off the board,\r
3980  *   return -2.  Otherwise map the event coordinate to the square.\r
3981  * The offset boardRect.left or boardRect.top must already have been\r
3982  *   subtracted from x.\r
3983  */\r
3984 int EventToSquare(x, limit)\r
3985      int x, limit;\r
3986 {\r
3987   if (x <= 0)\r
3988     return -2;\r
3989   if (x < lineGap)\r
3990     return -1;\r
3991   x -= lineGap;\r
3992   if ((x % (squareSize + lineGap)) >= squareSize)\r
3993     return -1;\r
3994   x /= (squareSize + lineGap);\r
3995     if (x >= limit)\r
3996     return -2;\r
3997   return x;\r
3998 }\r
3999 \r
4000 typedef struct {\r
4001   char piece;\r
4002   int command;\r
4003   char* name;\r
4004 } DropEnable;\r
4005 \r
4006 DropEnable dropEnables[] = {\r
4007   { 'P', DP_Pawn, N_("Pawn") },\r
4008   { 'N', DP_Knight, N_("Knight") },\r
4009   { 'B', DP_Bishop, N_("Bishop") },\r
4010   { 'R', DP_Rook, N_("Rook") },\r
4011   { 'Q', DP_Queen, N_("Queen") },\r
4012 };\r
4013 \r
4014 VOID\r
4015 SetupDropMenu(HMENU hmenu)\r
4016 {\r
4017   int i, count, enable;\r
4018   char *p;\r
4019   extern char white_holding[], black_holding[];\r
4020   char item[MSG_SIZ];\r
4021 \r
4022   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4023     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4024                dropEnables[i].piece);\r
4025     count = 0;\r
4026     while (p && *p++ == dropEnables[i].piece) count++;\r
4027       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4028     enable = count > 0 || !appData.testLegality\r
4029       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4030                       && !appData.icsActive);\r
4031     ModifyMenu(hmenu, dropEnables[i].command,\r
4032                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4033                dropEnables[i].command, item);\r
4034   }\r
4035 }\r
4036 \r
4037 void DragPieceBegin(int x, int y)\r
4038 {\r
4039       dragInfo.lastpos.x = boardRect.left + x;\r
4040       dragInfo.lastpos.y = boardRect.top + y;\r
4041       dragInfo.from.x = fromX;\r
4042       dragInfo.from.y = fromY;\r
4043       dragInfo.start = dragInfo.from;\r
4044       SetCapture(hwndMain);\r
4045 }\r
4046 \r
4047 void DragPieceEnd(int x, int y)\r
4048 {\r
4049     ReleaseCapture();\r
4050     dragInfo.start.x = dragInfo.start.y = -1;\r
4051     dragInfo.from = dragInfo.start;\r
4052     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4053 }\r
4054 \r
4055 /* Event handler for mouse messages */\r
4056 VOID\r
4057 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4058 {\r
4059   int x, y, menuNr;\r
4060   POINT pt;\r
4061   static int recursive = 0;\r
4062   HMENU hmenu;\r
4063   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4064 \r
4065   if (recursive) {\r
4066     if (message == WM_MBUTTONUP) {\r
4067       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4068          to the middle button: we simulate pressing the left button too!\r
4069          */\r
4070       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4071       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4072     }\r
4073     return;\r
4074   }\r
4075   recursive++;\r
4076   \r
4077   pt.x = LOWORD(lParam);\r
4078   pt.y = HIWORD(lParam);\r
4079   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4080   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4081   if (!flipView && y >= 0) {\r
4082     y = BOARD_HEIGHT - 1 - y;\r
4083   }\r
4084   if (flipView && x >= 0) {\r
4085     x = BOARD_WIDTH - 1 - x;\r
4086   }\r
4087 \r
4088   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4089 \r
4090   switch (message) {\r
4091   case WM_LBUTTONDOWN:\r
4092       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4093         if (gameMode == EditPosition) {\r
4094           SetWhiteToPlayEvent();\r
4095         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4096           AdjustClock(flipClock, -1);\r
4097         } else if (gameMode == IcsPlayingBlack ||\r
4098                    gameMode == MachinePlaysWhite) {\r
4099           CallFlagEvent();\r
4100         }\r
4101       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4102         if (gameMode == EditPosition) {\r
4103           SetBlackToPlayEvent();\r
4104         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4105           AdjustClock(!flipClock, -1);\r
4106         } else if (gameMode == IcsPlayingWhite ||\r
4107                    gameMode == MachinePlaysBlack) {\r
4108           CallFlagEvent();\r
4109         }\r
4110       }\r
4111       dragInfo.start.x = dragInfo.start.y = -1;\r
4112       dragInfo.from = dragInfo.start;\r
4113     if(fromX == -1 && frozen) { // not sure where this is for\r
4114                 fromX = fromY = -1; \r
4115       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4116       break;\r
4117     }\r
4118       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4119       DrawPosition(TRUE, NULL);\r
4120     break;\r
4121 \r
4122   case WM_LBUTTONUP:\r
4123       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4124       DrawPosition(TRUE, NULL);\r
4125     break;\r
4126 \r
4127   case WM_MOUSEMOVE:\r
4128     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4129     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4130     if ((appData.animateDragging || appData.highlightDragging)\r
4131         && (wParam & MK_LBUTTON)\r
4132         && dragInfo.from.x >= 0) \r
4133     {\r
4134       BOOL full_repaint = FALSE;\r
4135 \r
4136       if (appData.animateDragging) {\r
4137         dragInfo.pos = pt;\r
4138       }\r
4139       if (appData.highlightDragging) {\r
4140         SetHighlights(fromX, fromY, x, y);\r
4141         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4142             full_repaint = TRUE;\r
4143         }\r
4144       }\r
4145       \r
4146       DrawPosition( full_repaint, NULL);\r
4147       \r
4148       dragInfo.lastpos = dragInfo.pos;\r
4149     }\r
4150     break;\r
4151 \r
4152   case WM_MOUSEWHEEL: // [DM]\r
4153     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4154        /* Mouse Wheel is being rolled forward\r
4155         * Play moves forward\r
4156         */\r
4157        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4158                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4159        /* Mouse Wheel is being rolled backward\r
4160         * Play moves backward\r
4161         */\r
4162        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4163                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4164     }\r
4165     break;\r
4166 \r
4167   case WM_MBUTTONUP:\r
4168   case WM_RBUTTONUP:\r
4169     ReleaseCapture();\r
4170     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4171     break;\r
4172  \r
4173   case WM_MBUTTONDOWN:\r
4174   case WM_RBUTTONDOWN:\r
4175     ErrorPopDown();\r
4176     ReleaseCapture();\r
4177     fromX = fromY = -1;\r
4178     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4179     dragInfo.start.x = dragInfo.start.y = -1;\r
4180     dragInfo.from = dragInfo.start;\r
4181     dragInfo.lastpos = dragInfo.pos;\r
4182     if (appData.highlightDragging) {\r
4183       ClearHighlights();\r
4184     }\r
4185     if(y == -2) {\r
4186       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4187       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4188           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4189       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4190           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4191       }\r
4192       break;\r
4193     }\r
4194     DrawPosition(TRUE, NULL);\r
4195 \r
4196     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4197     switch (menuNr) {\r
4198     case 0:\r
4199       if (message == WM_MBUTTONDOWN) {\r
4200         buttonCount = 3;  /* even if system didn't think so */\r
4201         if (wParam & MK_SHIFT) \r
4202           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4203         else\r
4204           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4205       } else { /* message == WM_RBUTTONDOWN */\r
4206         /* Just have one menu, on the right button.  Windows users don't\r
4207            think to try the middle one, and sometimes other software steals\r
4208            it, or it doesn't really exist. */\r
4209         if(gameInfo.variant != VariantShogi)\r
4210             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4211         else\r
4212             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4213       }\r
4214       break;\r
4215     case 2:\r
4216       SetCapture(hwndMain);
4217       break;\r
4218     case 1:\r
4219       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4220       SetupDropMenu(hmenu);\r
4221       MenuPopup(hwnd, pt, hmenu, -1);\r
4222     default:\r
4223       break;\r
4224     }\r
4225     break;\r
4226   }\r
4227 \r
4228   recursive--;\r
4229 }\r
4230 \r
4231 /* Preprocess messages for buttons in main window */\r
4232 LRESULT CALLBACK\r
4233 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4234 {\r
4235   int id = GetWindowLong(hwnd, GWL_ID);\r
4236   int i, dir;\r
4237 \r
4238   for (i=0; i<N_BUTTONS; i++) {\r
4239     if (buttonDesc[i].id == id) break;\r
4240   }\r
4241   if (i == N_BUTTONS) return 0;\r
4242   switch (message) {\r
4243   case WM_KEYDOWN:\r
4244     switch (wParam) {\r
4245     case VK_LEFT:\r
4246     case VK_RIGHT:\r
4247       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4248       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4249       return TRUE;\r
4250     }\r
4251     break;\r
4252   case WM_CHAR:\r
4253     switch (wParam) {\r
4254     case '\r':\r
4255       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4256       return TRUE;\r
4257     default:\r
4258       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4259         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4260         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4261         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4262         SetFocus(h);\r
4263         SendMessage(h, WM_CHAR, wParam, lParam);\r
4264         return TRUE;\r
4265       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4266         PopUpMoveDialog((char)wParam);\r
4267       }\r
4268       break;\r
4269     }\r
4270     break;\r
4271   }\r
4272   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4273 }\r
4274 \r
4275 /* Process messages for Promotion dialog box */\r
4276 LRESULT CALLBACK\r
4277 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4278 {\r
4279   char promoChar;\r
4280 \r
4281   switch (message) {\r
4282   case WM_INITDIALOG: /* message: initialize dialog box */\r
4283     /* Center the dialog over the application window */\r
4284     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4285     Translate(hDlg, DLG_PromotionKing);\r
4286     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4287       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4288        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4289                SW_SHOW : SW_HIDE);\r
4290     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4291     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4292        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4293          PieceToChar(WhiteAngel) != '~') ||\r
4294         (PieceToChar(BlackAngel) >= 'A' &&\r
4295          PieceToChar(BlackAngel) != '~')   ) ?\r
4296                SW_SHOW : SW_HIDE);\r
4297     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4298        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4299          PieceToChar(WhiteMarshall) != '~') ||\r
4300         (PieceToChar(BlackMarshall) >= 'A' &&\r
4301          PieceToChar(BlackMarshall) != '~')   ) ?\r
4302                SW_SHOW : SW_HIDE);\r
4303     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4304     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4305        gameInfo.variant != VariantShogi ?\r
4306                SW_SHOW : SW_HIDE);\r
4307     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4308        gameInfo.variant != VariantShogi ?\r
4309                SW_SHOW : SW_HIDE);\r
4310     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4311        gameInfo.variant == VariantShogi ?\r
4312                SW_SHOW : SW_HIDE);\r
4313     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4314        gameInfo.variant == VariantShogi ?\r
4315                SW_SHOW : SW_HIDE);\r
4316     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4317        gameInfo.variant == VariantSuper ?\r
4318                SW_SHOW : SW_HIDE);\r
4319     return TRUE;\r
4320 \r
4321   case WM_COMMAND: /* message: received a command */\r
4322     switch (LOWORD(wParam)) {\r
4323     case IDCANCEL:\r
4324       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4325       ClearHighlights();\r
4326       DrawPosition(FALSE, NULL);\r
4327       return TRUE;\r
4328     case PB_King:\r
4329       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4330       break;\r
4331     case PB_Queen:\r
4332       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4333       break;\r
4334     case PB_Rook:\r
4335       promoChar = PieceToChar(BlackRook);\r
4336       break;\r
4337     case PB_Bishop:\r
4338       promoChar = PieceToChar(BlackBishop);\r
4339       break;\r
4340     case PB_Chancellor:\r
4341       promoChar = PieceToChar(BlackMarshall);\r
4342       break;\r
4343     case PB_Archbishop:\r
4344       promoChar = PieceToChar(BlackAngel);\r
4345       break;\r
4346     case PB_Knight:\r
4347       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4348       break;\r
4349     default:\r
4350       return FALSE;\r
4351     }\r
4352     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4353     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4354        only show the popup when we are already sure the move is valid or\r
4355        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4356        will figure out it is a promotion from the promoChar. */\r
4357     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4358     fromX = fromY = -1;\r
4359     if (!appData.highlightLastMove) {\r
4360       ClearHighlights();\r
4361       DrawPosition(FALSE, NULL);\r
4362     }\r
4363     return TRUE;\r
4364   }\r
4365   return FALSE;\r
4366 }\r
4367 \r
4368 /* Pop up promotion dialog */\r
4369 VOID\r
4370 PromotionPopup(HWND hwnd)\r
4371 {\r
4372   FARPROC lpProc;\r
4373 \r
4374   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4375   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4376     hwnd, (DLGPROC)lpProc);\r
4377   FreeProcInstance(lpProc);\r
4378 }\r
4379 \r
4380 void\r
4381 PromotionPopUp()\r
4382 {\r
4383   DrawPosition(TRUE, NULL);\r
4384   PromotionPopup(hwndMain);\r
4385 }\r
4386 \r
4387 /* Toggle ShowThinking */\r
4388 VOID\r
4389 ToggleShowThinking()\r
4390 {\r
4391   appData.showThinking = !appData.showThinking;\r
4392   ShowThinkingEvent();\r
4393 }\r
4394 \r
4395 VOID\r
4396 LoadGameDialog(HWND hwnd, char* title)\r
4397 {\r
4398   UINT number = 0;\r
4399   FILE *f;\r
4400   char fileTitle[MSG_SIZ];\r
4401   f = OpenFileDialog(hwnd, "rb", "",\r
4402                      appData.oldSaveStyle ? "gam" : "pgn",\r
4403                      GAME_FILT,\r
4404                      title, &number, fileTitle, NULL);\r
4405   if (f != NULL) {\r
4406     cmailMsgLoaded = FALSE;\r
4407     if (number == 0) {\r
4408       int error = GameListBuild(f);\r
4409       if (error) {\r
4410         DisplayError(_("Cannot build game list"), error);\r
4411       } else if (!ListEmpty(&gameList) &&\r
4412                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4413         GameListPopUp(f, fileTitle);\r
4414         return;\r
4415       }\r
4416       GameListDestroy();\r
4417       number = 1;\r
4418     }\r
4419     LoadGame(f, number, fileTitle, FALSE);\r
4420   }\r
4421 }\r
4422 \r
4423 int get_term_width()\r
4424 {\r
4425     HDC hdc;\r
4426     TEXTMETRIC tm;\r
4427     RECT rc;\r
4428     HFONT hfont, hold_font;\r
4429     LOGFONT lf;\r
4430     HWND hText;\r
4431 \r
4432     if (hwndConsole)\r
4433         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4434     else\r
4435         return 79;\r
4436 \r
4437     // get the text metrics\r
4438     hdc = GetDC(hText);\r
4439     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4440     if (consoleCF.dwEffects & CFE_BOLD)\r
4441         lf.lfWeight = FW_BOLD;\r
4442     if (consoleCF.dwEffects & CFE_ITALIC)\r
4443         lf.lfItalic = TRUE;\r
4444     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4445         lf.lfStrikeOut = TRUE;\r
4446     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4447         lf.lfUnderline = TRUE;\r
4448     hfont = CreateFontIndirect(&lf);\r
4449     hold_font = SelectObject(hdc, hfont);\r
4450     GetTextMetrics(hdc, &tm);\r
4451     SelectObject(hdc, hold_font);\r
4452     DeleteObject(hfont);\r
4453     ReleaseDC(hText, hdc);\r
4454 \r
4455     // get the rectangle\r
4456     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4457 \r
4458     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4459 }\r
4460 \r
4461 void UpdateICSWidth(HWND hText)\r
4462 {\r
4463     LONG old_width, new_width;\r
4464 \r
4465     new_width = get_term_width(hText, FALSE);\r
4466     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4467     if (new_width != old_width)\r
4468     {\r
4469         ics_update_width(new_width);\r
4470         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4471     }\r
4472 }\r
4473 \r
4474 VOID\r
4475 ChangedConsoleFont()\r
4476 {\r
4477   CHARFORMAT cfmt;\r
4478   CHARRANGE tmpsel, sel;\r
4479   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4480   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4481   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4482   PARAFORMAT paraf;\r
4483 \r
4484   cfmt.cbSize = sizeof(CHARFORMAT);\r
4485   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4486     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4487                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4488   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4489    * size.  This was undocumented in the version of MSVC++ that I had\r
4490    * when I wrote the code, but is apparently documented now.\r
4491    */\r
4492   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4493   cfmt.bCharSet = f->lf.lfCharSet;\r
4494   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4495   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4496   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4497   /* Why are the following seemingly needed too? */\r
4498   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4499   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4500   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4501   tmpsel.cpMin = 0;\r
4502   tmpsel.cpMax = -1; /*999999?*/\r
4503   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4504   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4505   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4506    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4507    */\r
4508   paraf.cbSize = sizeof(paraf);\r
4509   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4510   paraf.dxStartIndent = 0;\r
4511   paraf.dxOffset = WRAP_INDENT;\r
4512   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4513   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4514   UpdateICSWidth(hText);\r
4515 }\r
4516 \r
4517 /*---------------------------------------------------------------------------*\\r
4518  *\r
4519  * Window Proc for main window\r
4520  *\r
4521 \*---------------------------------------------------------------------------*/\r
4522 \r
4523 /* Process messages for main window, etc. */\r
4524 LRESULT CALLBACK\r
4525 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4526 {\r
4527   FARPROC lpProc;\r
4528   int wmId, wmEvent;\r
4529   char *defName;\r
4530   FILE *f;\r
4531   UINT number;\r
4532   char fileTitle[MSG_SIZ];\r
4533   char buf[MSG_SIZ];\r
4534   static SnapData sd;\r
4535 \r
4536   switch (message) {\r
4537 \r
4538   case WM_PAINT: /* message: repaint portion of window */\r
4539     PaintProc(hwnd);\r
4540     break;\r
4541 \r
4542   case WM_ERASEBKGND:\r
4543     if (IsIconic(hwnd)) {\r
4544       /* Cheat; change the message */\r
4545       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4546     } else {\r
4547       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4548     }\r
4549     break;\r
4550 \r
4551   case WM_LBUTTONDOWN:\r
4552   case WM_MBUTTONDOWN:\r
4553   case WM_RBUTTONDOWN:\r
4554   case WM_LBUTTONUP:\r
4555   case WM_MBUTTONUP:\r
4556   case WM_RBUTTONUP:\r
4557   case WM_MOUSEMOVE:\r
4558   case WM_MOUSEWHEEL:\r
4559     MouseEvent(hwnd, message, wParam, lParam);\r
4560     break;\r
4561 \r
4562   JAWS_KB_NAVIGATION\r
4563 \r
4564   case WM_CHAR:\r
4565     \r
4566     JAWS_ALT_INTERCEPT\r
4567 \r
4568     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4569         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4570         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4571         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4572         SetFocus(h);\r
4573         SendMessage(h, message, wParam, lParam);\r
4574     } else if(lParam != KF_REPEAT) {\r
4575         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4576                 PopUpMoveDialog((char)wParam);\r
4577         } else if((char)wParam == 003) CopyGameToClipboard();\r
4578          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4579     }\r
4580 \r
4581     break;\r
4582 \r
4583   case WM_PALETTECHANGED:\r
4584     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4585       int nnew;\r
4586       HDC hdc = GetDC(hwndMain);\r
4587       SelectPalette(hdc, hPal, TRUE);\r
4588       nnew = RealizePalette(hdc);\r
4589       if (nnew > 0) {\r
4590         paletteChanged = TRUE;\r
4591         InvalidateRect(hwnd, &boardRect, FALSE);\r
4592       }\r
4593       ReleaseDC(hwnd, hdc);\r
4594     }\r
4595     break;\r
4596 \r
4597   case WM_QUERYNEWPALETTE:\r
4598     if (!appData.monoMode /*&& paletteChanged*/) {\r
4599       int nnew;\r
4600       HDC hdc = GetDC(hwndMain);\r
4601       paletteChanged = FALSE;\r
4602       SelectPalette(hdc, hPal, FALSE);\r
4603       nnew = RealizePalette(hdc);\r
4604       if (nnew > 0) {\r
4605         InvalidateRect(hwnd, &boardRect, FALSE);\r
4606       }\r
4607       ReleaseDC(hwnd, hdc);\r
4608       return TRUE;\r
4609     }\r
4610     return FALSE;\r
4611 \r
4612   case WM_COMMAND: /* message: command from application menu */\r
4613     wmId    = LOWORD(wParam);\r
4614     wmEvent = HIWORD(wParam);\r
4615 \r
4616     switch (wmId) {\r
4617     case IDM_NewGame:\r
4618       ResetGameEvent();\r
4619       SAY("new game enter a move to play against the computer with white");\r
4620       break;\r
4621 \r
4622     case IDM_NewGameFRC:\r
4623       if( NewGameFRC() == 0 ) {\r
4624         ResetGameEvent();\r
4625       }\r
4626       break;\r
4627 \r
4628     case IDM_NewVariant:\r
4629       NewVariantPopup(hwnd);\r
4630       break;\r
4631 \r
4632     case IDM_LoadGame:\r
4633       LoadGameDialog(hwnd, _("Load Game from File"));\r
4634       break;\r
4635 \r
4636     case IDM_LoadNextGame:\r
4637       ReloadGame(1);\r
4638       break;\r
4639 \r
4640     case IDM_LoadPrevGame:\r
4641       ReloadGame(-1);\r
4642       break;\r
4643 \r
4644     case IDM_ReloadGame:\r
4645       ReloadGame(0);\r
4646       break;\r
4647 \r
4648     case IDM_LoadPosition:\r
4649       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4650         Reset(FALSE, TRUE);\r
4651       }\r
4652       number = 1;\r
4653       f = OpenFileDialog(hwnd, "rb", "",\r
4654                          appData.oldSaveStyle ? "pos" : "fen",\r
4655                          POSITION_FILT,\r
4656                          _("Load Position from File"), &number, fileTitle, NULL);\r
4657       if (f != NULL) {\r
4658         LoadPosition(f, number, fileTitle);\r
4659       }\r
4660       break;\r
4661 \r
4662     case IDM_LoadNextPosition:\r
4663       ReloadPosition(1);\r
4664       break;\r
4665 \r
4666     case IDM_LoadPrevPosition:\r
4667       ReloadPosition(-1);\r
4668       break;\r
4669 \r
4670     case IDM_ReloadPosition:\r
4671       ReloadPosition(0);\r
4672       break;\r
4673 \r
4674     case IDM_SaveGame:\r
4675       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4676       f = OpenFileDialog(hwnd, "a", defName,\r
4677                          appData.oldSaveStyle ? "gam" : "pgn",\r
4678                          GAME_FILT,\r
4679                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4680       if (f != NULL) {\r
4681         SaveGame(f, 0, "");\r
4682       }\r
4683       break;\r
4684 \r
4685     case IDM_SavePosition:\r
4686       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4687       f = OpenFileDialog(hwnd, "a", defName,\r
4688                          appData.oldSaveStyle ? "pos" : "fen",\r
4689                          POSITION_FILT,\r
4690                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4691       if (f != NULL) {\r
4692         SavePosition(f, 0, "");\r
4693       }\r
4694       break;\r
4695 \r
4696     case IDM_SaveDiagram:\r
4697       defName = "diagram";\r
4698       f = OpenFileDialog(hwnd, "wb", defName,\r
4699                          "bmp",\r
4700                          DIAGRAM_FILT,\r
4701                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4702       if (f != NULL) {\r
4703         SaveDiagram(f);\r
4704       }\r
4705       break;\r
4706 \r
4707     case IDM_CopyGame:\r
4708       CopyGameToClipboard();\r
4709       break;\r
4710 \r
4711     case IDM_PasteGame:\r
4712       PasteGameFromClipboard();\r
4713       break;\r
4714 \r
4715     case IDM_CopyGameListToClipboard:\r
4716       CopyGameListToClipboard();\r
4717       break;\r
4718 \r
4719     /* [AS] Autodetect FEN or PGN data */\r
4720     case IDM_PasteAny:\r
4721       PasteGameOrFENFromClipboard();\r
4722       break;\r
4723 \r
4724     /* [AS] Move history */\r
4725     case IDM_ShowMoveHistory:\r
4726         if( MoveHistoryIsUp() ) {\r
4727             MoveHistoryPopDown();\r
4728         }\r
4729         else {\r
4730             MoveHistoryPopUp();\r
4731         }\r
4732         break;\r
4733 \r
4734     /* [AS] Eval graph */\r
4735     case IDM_ShowEvalGraph:\r
4736         if( EvalGraphIsUp() ) {\r
4737             EvalGraphPopDown();\r
4738         }\r
4739         else {\r
4740             EvalGraphPopUp();\r
4741             SetFocus(hwndMain);\r
4742         }\r
4743         break;\r
4744 \r
4745     /* [AS] Engine output */\r
4746     case IDM_ShowEngineOutput:\r
4747         if( EngineOutputIsUp() ) {\r
4748             EngineOutputPopDown();\r
4749         }\r
4750         else {\r
4751             EngineOutputPopUp();\r
4752         }\r
4753         break;\r
4754 \r
4755     /* [AS] User adjudication */\r
4756     case IDM_UserAdjudication_White:\r
4757         UserAdjudicationEvent( +1 );\r
4758         break;\r
4759 \r
4760     case IDM_UserAdjudication_Black:\r
4761         UserAdjudicationEvent( -1 );\r
4762         break;\r
4763 \r
4764     case IDM_UserAdjudication_Draw:\r
4765         UserAdjudicationEvent( 0 );\r
4766         break;\r
4767 \r
4768     /* [AS] Game list options dialog */\r
4769     case IDM_GameListOptions:\r
4770       GameListOptions();\r
4771       break;\r
4772 \r
4773     case IDM_NewChat:\r
4774       ChatPopUp(NULL);\r
4775       break;\r
4776 \r
4777     case IDM_CopyPosition:\r
4778       CopyFENToClipboard();\r
4779       break;\r
4780 \r
4781     case IDM_PastePosition:\r
4782       PasteFENFromClipboard();\r
4783       break;\r
4784 \r
4785     case IDM_MailMove:\r
4786       MailMoveEvent();\r
4787       break;\r
4788 \r
4789     case IDM_ReloadCMailMsg:\r
4790       Reset(TRUE, TRUE);\r
4791       ReloadCmailMsgEvent(FALSE);\r
4792       break;\r
4793 \r
4794     case IDM_Minimize:\r
4795       ShowWindow(hwnd, SW_MINIMIZE);\r
4796       break;\r
4797 \r
4798     case IDM_Exit:\r
4799       ExitEvent(0);\r
4800       break;\r
4801 \r
4802     case IDM_MachineWhite:\r
4803       MachineWhiteEvent();\r
4804       /*\r
4805        * refresh the tags dialog only if it's visible\r
4806        */\r
4807       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4808           char *tags;\r
4809           tags = PGNTags(&gameInfo);\r
4810           TagsPopUp(tags, CmailMsg());\r
4811           free(tags);\r
4812       }\r
4813       SAY("computer starts playing white");\r
4814       break;\r
4815 \r
4816     case IDM_MachineBlack:\r
4817       MachineBlackEvent();\r
4818       /*\r
4819        * refresh the tags dialog only if it's visible\r
4820        */\r
4821       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4822           char *tags;\r
4823           tags = PGNTags(&gameInfo);\r
4824           TagsPopUp(tags, CmailMsg());\r
4825           free(tags);\r
4826       }\r
4827       SAY("computer starts playing black");\r
4828       break;\r
4829 \r
4830     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4831       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4832         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4833       }\r
4834       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4835       appData.matchGames = appData.defaultMatchGames;\r
4836       matchGame = 1;\r
4837 \r
4838     case IDM_TwoMachines:\r
4839       TwoMachinesEvent();\r
4840       /*\r
4841        * refresh the tags dialog only if it's visible\r
4842        */\r
4843       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4844           char *tags;\r
4845           tags = PGNTags(&gameInfo);\r
4846           TagsPopUp(tags, CmailMsg());\r
4847           free(tags);\r
4848       }\r
4849       SAY("computer starts playing both sides");\r
4850       break;\r
4851 \r
4852     case IDM_AnalysisMode:\r
4853       if (!first.analysisSupport) {\r
4854         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4855         DisplayError(buf, 0);\r
4856       } else {\r
4857         SAY("analyzing current position");\r
4858         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4859         if (appData.icsActive) {\r
4860                if (gameMode != IcsObserving) {\r
4861                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4862                        DisplayError(buf, 0);\r
4863                        /* secure check */\r
4864                        if (appData.icsEngineAnalyze) {\r
4865                                if (appData.debugMode) \r
4866                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4867                                ExitAnalyzeMode();\r
4868                                ModeHighlight();\r
4869                                break;\r
4870                        }\r
4871                        break;\r
4872                } else {\r
4873                        /* if enable, user want disable icsEngineAnalyze */\r
4874                        if (appData.icsEngineAnalyze) {\r
4875                                ExitAnalyzeMode();\r
4876                                ModeHighlight();\r
4877                                break;\r
4878                        }\r
4879                        appData.icsEngineAnalyze = TRUE;\r
4880                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4881                }\r
4882         } \r
4883         if (!appData.showThinking) ToggleShowThinking();\r
4884         AnalyzeModeEvent();\r
4885       }\r
4886       break;\r
4887 \r
4888     case IDM_AnalyzeFile:\r
4889       if (!first.analysisSupport) {\r
4890         char buf[MSG_SIZ];\r
4891           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4892         DisplayError(buf, 0);\r
4893       } else {\r
4894         if (!appData.showThinking) ToggleShowThinking();\r
4895         AnalyzeFileEvent();\r
4896         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4897         AnalysisPeriodicEvent(1);\r
4898       }\r
4899       break;\r
4900 \r
4901     case IDM_IcsClient:\r
4902       IcsClientEvent();\r
4903       break;\r
4904 \r
4905     case IDM_EditGame:\r
4906       EditGameEvent();\r
4907       SAY("edit game");\r
4908       break;\r
4909 \r
4910     case IDM_EditPosition:\r
4911       EditPositionEvent();\r
4912       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4913       break;\r
4914 \r
4915     case IDM_Training:\r
4916       TrainingEvent();\r
4917       break;\r
4918 \r
4919     case IDM_ShowGameList:\r
4920       ShowGameListProc();\r
4921       break;\r
4922 \r
4923     case IDM_EditTags:\r
4924       EditTagsProc();\r
4925       break;\r
4926 \r
4927     case IDM_EditComment:\r
4928       if (commentUp && editComment) {\r
4929         CommentPopDown();\r
4930       } else {\r
4931         EditCommentEvent();\r
4932       }\r
4933       break;\r
4934 \r
4935     case IDM_Pause:\r
4936       PauseEvent();\r
4937       break;\r
4938 \r
4939     case IDM_Accept:\r
4940       AcceptEvent();\r
4941       break;\r
4942 \r
4943     case IDM_Decline:\r
4944       DeclineEvent();\r
4945       break;\r
4946 \r
4947     case IDM_Rematch:\r
4948       RematchEvent();\r
4949       break;\r
4950 \r
4951     case IDM_CallFlag:\r
4952       CallFlagEvent();\r
4953       break;\r
4954 \r
4955     case IDM_Draw:\r
4956       DrawEvent();\r
4957       break;\r
4958 \r
4959     case IDM_Adjourn:\r
4960       AdjournEvent();\r
4961       break;\r
4962 \r
4963     case IDM_Abort:\r
4964       AbortEvent();\r
4965       break;\r
4966 \r
4967     case IDM_Resign:\r
4968       ResignEvent();\r
4969       break;\r
4970 \r
4971     case IDM_StopObserving:\r
4972       StopObservingEvent();\r
4973       break;\r
4974 \r
4975     case IDM_StopExamining:\r
4976       StopExaminingEvent();\r
4977       break;\r
4978 \r
4979     case IDM_Upload:\r
4980       UploadGameEvent();\r
4981       break;\r
4982 \r
4983     case IDM_TypeInMove:\r
4984       PopUpMoveDialog('\000');\r
4985       break;\r
4986 \r
4987     case IDM_TypeInName:\r
4988       PopUpNameDialog('\000');\r
4989       break;\r
4990 \r
4991     case IDM_Backward:\r
4992       BackwardEvent();\r
4993       SetFocus(hwndMain);\r
4994       break;\r
4995 \r
4996     JAWS_MENU_ITEMS\r
4997 \r
4998     case IDM_Forward:\r
4999       ForwardEvent();\r
5000       SetFocus(hwndMain);\r
5001       break;\r
5002 \r
5003     case IDM_ToStart:\r
5004       ToStartEvent();\r
5005       SetFocus(hwndMain);\r
5006       break;\r
5007 \r
5008     case IDM_ToEnd:\r
5009       ToEndEvent();\r
5010       SetFocus(hwndMain);\r
5011       break;\r
5012 \r
5013     case IDM_Revert:\r
5014       RevertEvent(FALSE);\r
5015       break;\r
5016 \r
5017     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5018       RevertEvent(TRUE);\r
5019       break;\r
5020 \r
5021     case IDM_TruncateGame:\r
5022       TruncateGameEvent();\r
5023       break;\r
5024 \r
5025     case IDM_MoveNow:\r
5026       MoveNowEvent();\r
5027       break;\r
5028 \r
5029     case IDM_RetractMove:\r
5030       RetractMoveEvent();\r
5031       break;\r
5032 \r
5033     case IDM_FlipView:\r
5034       flipView = !flipView;\r
5035       DrawPosition(FALSE, NULL);\r
5036       break;\r
5037 \r
5038     case IDM_FlipClock:\r
5039       flipClock = !flipClock;\r
5040       DisplayBothClocks();\r
5041       DrawPosition(FALSE, NULL);\r
5042       break;\r
5043 \r
5044     case IDM_MuteSounds:\r
5045       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5046       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5047                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5048       break;\r
5049 \r
5050     case IDM_GeneralOptions:\r
5051       GeneralOptionsPopup(hwnd);\r
5052       DrawPosition(TRUE, NULL);\r
5053       break;\r
5054 \r
5055     case IDM_BoardOptions:\r
5056       BoardOptionsPopup(hwnd);\r
5057       break;\r
5058 \r
5059     case IDM_EnginePlayOptions:\r
5060       EnginePlayOptionsPopup(hwnd);\r
5061       break;\r
5062 \r
5063     case IDM_Engine1Options:\r
5064       EngineOptionsPopup(hwnd, &first);\r
5065       break;\r
5066 \r
5067     case IDM_Engine2Options:\r
5068       savedHwnd = hwnd;\r
5069       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5070       EngineOptionsPopup(hwnd, &second);\r
5071       break;\r
5072 \r
5073     case IDM_OptionsUCI:\r
5074       UciOptionsPopup(hwnd);\r
5075       break;\r
5076 \r
5077     case IDM_IcsOptions:\r
5078       IcsOptionsPopup(hwnd);\r
5079       break;\r
5080 \r
5081     case IDM_Fonts:\r
5082       FontsOptionsPopup(hwnd);\r
5083       break;\r
5084 \r
5085     case IDM_Sounds:\r
5086       SoundOptionsPopup(hwnd);\r
5087       break;\r
5088 \r
5089     case IDM_CommPort:\r
5090       CommPortOptionsPopup(hwnd);\r
5091       break;\r
5092 \r
5093     case IDM_LoadOptions:\r
5094       LoadOptionsPopup(hwnd);\r
5095       break;\r
5096 \r
5097     case IDM_SaveOptions:\r
5098       SaveOptionsPopup(hwnd);\r
5099       break;\r
5100 \r
5101     case IDM_TimeControl:\r
5102       TimeControlOptionsPopup(hwnd);\r
5103       break;\r
5104 \r
5105     case IDM_SaveSettings:\r
5106       SaveSettings(settingsFileName);\r
5107       break;\r
5108 \r
5109     case IDM_SaveSettingsOnExit:\r
5110       saveSettingsOnExit = !saveSettingsOnExit;\r
5111       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5112                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5113                                          MF_CHECKED : MF_UNCHECKED));\r
5114       break;\r
5115 \r
5116     case IDM_Hint:\r
5117       HintEvent();\r
5118       break;\r
5119 \r
5120     case IDM_Book:\r
5121       BookEvent();\r
5122       break;\r
5123 \r
5124     case IDM_AboutGame:\r
5125       AboutGameEvent();\r
5126       break;\r
5127 \r
5128     case IDM_Debug:\r
5129       appData.debugMode = !appData.debugMode;\r
5130       if (appData.debugMode) {\r
5131         char dir[MSG_SIZ];\r
5132         GetCurrentDirectory(MSG_SIZ, dir);\r
5133         SetCurrentDirectory(installDir);\r
5134         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5135         SetCurrentDirectory(dir);\r
5136         setbuf(debugFP, NULL);\r
5137       } else {\r
5138         fclose(debugFP);\r
5139         debugFP = NULL;\r
5140       }\r
5141       break;\r
5142 \r
5143     case IDM_HELPCONTENTS:\r
5144       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5145           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5146           MessageBox (GetFocus(),\r
5147                     _("Unable to activate help"),\r
5148                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5149       }\r
5150       break;\r
5151 \r
5152     case IDM_HELPSEARCH:\r
5153         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5154             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5155         MessageBox (GetFocus(),\r
5156                     _("Unable to activate help"),\r
5157                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5158       }\r
5159       break;\r
5160 \r
5161     case IDM_HELPHELP:\r
5162       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5163         MessageBox (GetFocus(),\r
5164                     _("Unable to activate help"),\r
5165                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5166       }\r
5167       break;\r
5168 \r
5169     case IDM_ABOUT:\r
5170       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5171       DialogBox(hInst, \r
5172         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5173         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5174       FreeProcInstance(lpProc);\r
5175       break;\r
5176 \r
5177     case IDM_DirectCommand1:\r
5178       AskQuestionEvent(_("Direct Command"),\r
5179                        _("Send to chess program:"), "", "1");\r
5180       break;\r
5181     case IDM_DirectCommand2:\r
5182       AskQuestionEvent(_("Direct Command"),\r
5183                        _("Send to second chess program:"), "", "2");\r
5184       break;\r
5185 \r
5186     case EP_WhitePawn:\r
5187       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5188       fromX = fromY = -1;\r
5189       break;\r
5190 \r
5191     case EP_WhiteKnight:\r
5192       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5193       fromX = fromY = -1;\r
5194       break;\r
5195 \r
5196     case EP_WhiteBishop:\r
5197       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5198       fromX = fromY = -1;\r
5199       break;\r
5200 \r
5201     case EP_WhiteRook:\r
5202       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5203       fromX = fromY = -1;\r
5204       break;\r
5205 \r
5206     case EP_WhiteQueen:\r
5207       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5208       fromX = fromY = -1;\r
5209       break;\r
5210 \r
5211     case EP_WhiteFerz:\r
5212       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5213       fromX = fromY = -1;\r
5214       break;\r
5215 \r
5216     case EP_WhiteWazir:\r
5217       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5218       fromX = fromY = -1;\r
5219       break;\r
5220 \r
5221     case EP_WhiteAlfil:\r
5222       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5223       fromX = fromY = -1;\r
5224       break;\r
5225 \r
5226     case EP_WhiteCannon:\r
5227       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5228       fromX = fromY = -1;\r
5229       break;\r
5230 \r
5231     case EP_WhiteCardinal:\r
5232       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5233       fromX = fromY = -1;\r
5234       break;\r
5235 \r
5236     case EP_WhiteMarshall:\r
5237       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5238       fromX = fromY = -1;\r
5239       break;\r
5240 \r
5241     case EP_WhiteKing:\r
5242       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5243       fromX = fromY = -1;\r
5244       break;\r
5245 \r
5246     case EP_BlackPawn:\r
5247       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5248       fromX = fromY = -1;\r
5249       break;\r
5250 \r
5251     case EP_BlackKnight:\r
5252       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5253       fromX = fromY = -1;\r
5254       break;\r
5255 \r
5256     case EP_BlackBishop:\r
5257       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5258       fromX = fromY = -1;\r
5259       break;\r
5260 \r
5261     case EP_BlackRook:\r
5262       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5263       fromX = fromY = -1;\r
5264       break;\r
5265 \r
5266     case EP_BlackQueen:\r
5267       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5268       fromX = fromY = -1;\r
5269       break;\r
5270 \r
5271     case EP_BlackFerz:\r
5272       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5273       fromX = fromY = -1;\r
5274       break;\r
5275 \r
5276     case EP_BlackWazir:\r
5277       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5278       fromX = fromY = -1;\r
5279       break;\r
5280 \r
5281     case EP_BlackAlfil:\r
5282       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5283       fromX = fromY = -1;\r
5284       break;\r
5285 \r
5286     case EP_BlackCannon:\r
5287       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5288       fromX = fromY = -1;\r
5289       break;\r
5290 \r
5291     case EP_BlackCardinal:\r
5292       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5293       fromX = fromY = -1;\r
5294       break;\r
5295 \r
5296     case EP_BlackMarshall:\r
5297       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5298       fromX = fromY = -1;\r
5299       break;\r
5300 \r
5301     case EP_BlackKing:\r
5302       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5303       fromX = fromY = -1;\r
5304       break;\r
5305 \r
5306     case EP_EmptySquare:\r
5307       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5308       fromX = fromY = -1;\r
5309       break;\r
5310 \r
5311     case EP_ClearBoard:\r
5312       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5313       fromX = fromY = -1;\r
5314       break;\r
5315 \r
5316     case EP_White:\r
5317       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5318       fromX = fromY = -1;\r
5319       break;\r
5320 \r
5321     case EP_Black:\r
5322       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5323       fromX = fromY = -1;\r
5324       break;\r
5325 \r
5326     case EP_Promote:\r
5327       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5328       fromX = fromY = -1;\r
5329       break;\r
5330 \r
5331     case EP_Demote:\r
5332       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5333       fromX = fromY = -1;\r
5334       break;\r
5335 \r
5336     case DP_Pawn:\r
5337       DropMenuEvent(WhitePawn, fromX, fromY);\r
5338       fromX = fromY = -1;\r
5339       break;\r
5340 \r
5341     case DP_Knight:\r
5342       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5343       fromX = fromY = -1;\r
5344       break;\r
5345 \r
5346     case DP_Bishop:\r
5347       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5348       fromX = fromY = -1;\r
5349       break;\r
5350 \r
5351     case DP_Rook:\r
5352       DropMenuEvent(WhiteRook, fromX, fromY);\r
5353       fromX = fromY = -1;\r
5354       break;\r
5355 \r
5356     case DP_Queen:\r
5357       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5358       fromX = fromY = -1;\r
5359       break;\r
5360 \r
5361     case IDM_English:\r
5362       barbaric = 0;\r
5363       TranslateMenus(0);\r
5364       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5365       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5366       lastChecked = wmId;\r
5367       break;\r
5368 \r
5369     default:\r
5370       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5371           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5372           TranslateMenus(0);\r
5373           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5374           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5375           lastChecked = wmId;\r
5376           break;\r
5377       }\r
5378       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5379     }\r
5380     break;\r
5381 \r
5382   case WM_TIMER:\r
5383     switch (wParam) {\r
5384     case CLOCK_TIMER_ID:\r
5385       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5386       clockTimerEvent = 0;\r
5387       DecrementClocks(); /* call into back end */\r
5388       break;\r
5389     case LOAD_GAME_TIMER_ID:\r
5390       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5391       loadGameTimerEvent = 0;\r
5392       AutoPlayGameLoop(); /* call into back end */\r
5393       break;\r
5394     case ANALYSIS_TIMER_ID:\r
5395       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5396                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5397         AnalysisPeriodicEvent(0);\r
5398       } else {\r
5399         KillTimer(hwnd, analysisTimerEvent);\r
5400         analysisTimerEvent = 0;\r
5401       }\r
5402       break;\r
5403     case DELAYED_TIMER_ID:\r
5404       KillTimer(hwnd, delayedTimerEvent);\r
5405       delayedTimerEvent = 0;\r
5406       delayedTimerCallback();\r
5407       break;\r
5408     }\r
5409     break;\r
5410 \r
5411   case WM_USER_Input:\r
5412     InputEvent(hwnd, message, wParam, lParam);\r
5413     break;\r
5414 \r
5415   /* [AS] Also move "attached" child windows */\r
5416   case WM_WINDOWPOSCHANGING:\r
5417 \r
5418     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5419         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5420 \r
5421         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5422             /* Window is moving */\r
5423             RECT rcMain;\r
5424 \r
5425 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5426             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5427             rcMain.right  = wpMain.x + wpMain.width;\r
5428             rcMain.top    = wpMain.y;\r
5429             rcMain.bottom = wpMain.y + wpMain.height;\r
5430             \r
5431             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5432             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5433             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5434             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5435             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5436             wpMain.x = lpwp->x;\r
5437             wpMain.y = lpwp->y;\r
5438         }\r
5439     }\r
5440     break;\r
5441 \r
5442   /* [AS] Snapping */\r
5443   case WM_ENTERSIZEMOVE:\r
5444     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5445     if (hwnd == hwndMain) {\r
5446       doingSizing = TRUE;\r
5447       lastSizing = 0;\r
5448     }\r
5449     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5450     break;\r
5451 \r
5452   case WM_SIZING:\r
5453     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5454     if (hwnd == hwndMain) {\r
5455       lastSizing = wParam;\r
5456     }\r
5457     break;\r
5458 \r
5459   case WM_MOVING:\r
5460     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5461       return OnMoving( &sd, hwnd, wParam, lParam );\r
5462 \r
5463   case WM_EXITSIZEMOVE:\r
5464     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5465     if (hwnd == hwndMain) {\r
5466       RECT client;\r
5467       doingSizing = FALSE;\r
5468       InvalidateRect(hwnd, &boardRect, FALSE);\r
5469       GetClientRect(hwnd, &client);\r
5470       ResizeBoard(client.right, client.bottom, lastSizing);\r
5471       lastSizing = 0;\r
5472       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5473     }\r
5474     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5475     break;\r
5476 \r
5477   case WM_DESTROY: /* message: window being destroyed */\r
5478     PostQuitMessage(0);\r
5479     break;\r
5480 \r
5481   case WM_CLOSE:\r
5482     if (hwnd == hwndMain) {\r
5483       ExitEvent(0);\r
5484     }\r
5485     break;\r
5486 \r
5487   default:      /* Passes it on if unprocessed */\r
5488     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5489   }\r
5490   return 0;\r
5491 }\r
5492 \r
5493 /*---------------------------------------------------------------------------*\\r
5494  *\r
5495  * Misc utility routines\r
5496  *\r
5497 \*---------------------------------------------------------------------------*/\r
5498 \r
5499 /*\r
5500  * Decent random number generator, at least not as bad as Windows\r
5501  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5502  */\r
5503 unsigned int randstate;\r
5504 \r
5505 int\r
5506 myrandom(void)\r
5507 {\r
5508   randstate = randstate * 1664525 + 1013904223;\r
5509   return (int) randstate & 0x7fffffff;\r
5510 }\r
5511 \r
5512 void\r
5513 mysrandom(unsigned int seed)\r
5514 {\r
5515   randstate = seed;\r
5516 }\r
5517 \r
5518 \r
5519 /* \r
5520  * returns TRUE if user selects a different color, FALSE otherwise \r
5521  */\r
5522 \r
5523 BOOL\r
5524 ChangeColor(HWND hwnd, COLORREF *which)\r
5525 {\r
5526   static BOOL firstTime = TRUE;\r
5527   static DWORD customColors[16];\r
5528   CHOOSECOLOR cc;\r
5529   COLORREF newcolor;\r
5530   int i;\r
5531   ColorClass ccl;\r
5532 \r
5533   if (firstTime) {\r
5534     /* Make initial colors in use available as custom colors */\r
5535     /* Should we put the compiled-in defaults here instead? */\r
5536     i = 0;\r
5537     customColors[i++] = lightSquareColor & 0xffffff;\r
5538     customColors[i++] = darkSquareColor & 0xffffff;\r
5539     customColors[i++] = whitePieceColor & 0xffffff;\r
5540     customColors[i++] = blackPieceColor & 0xffffff;\r
5541     customColors[i++] = highlightSquareColor & 0xffffff;\r
5542     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5543 \r
5544     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5545       customColors[i++] = textAttribs[ccl].color;\r
5546     }\r
5547     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5548     firstTime = FALSE;\r
5549   }\r
5550 \r
5551   cc.lStructSize = sizeof(cc);\r
5552   cc.hwndOwner = hwnd;\r
5553   cc.hInstance = NULL;\r
5554   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5555   cc.lpCustColors = (LPDWORD) customColors;\r
5556   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5557 \r
5558   if (!ChooseColor(&cc)) return FALSE;\r
5559 \r
5560   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5561   if (newcolor == *which) return FALSE;\r
5562   *which = newcolor;\r
5563   return TRUE;\r
5564 \r
5565   /*\r
5566   InitDrawingColors();\r
5567   InvalidateRect(hwnd, &boardRect, FALSE);\r
5568   */\r
5569 }\r
5570 \r
5571 BOOLEAN\r
5572 MyLoadSound(MySound *ms)\r
5573 {\r
5574   BOOL ok = FALSE;\r
5575   struct stat st;\r
5576   FILE *f;\r
5577 \r
5578   if (ms->data) free(ms->data);\r
5579   ms->data = NULL;\r
5580 \r
5581   switch (ms->name[0]) {\r
5582   case NULLCHAR:\r
5583     /* Silence */\r
5584     ok = TRUE;\r
5585     break;\r
5586   case '$':\r
5587     /* System sound from Control Panel.  Don't preload here. */\r
5588     ok = TRUE;\r
5589     break;\r
5590   case '!':\r
5591     if (ms->name[1] == NULLCHAR) {\r
5592       /* "!" alone = silence */\r
5593       ok = TRUE;\r
5594     } else {\r
5595       /* Builtin wave resource.  Error if not found. */\r
5596       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5597       if (h == NULL) break;\r
5598       ms->data = (void *)LoadResource(hInst, h);\r
5599       if (h == NULL) break;\r
5600       ok = TRUE;\r
5601     }\r
5602     break;\r
5603   default:\r
5604     /* .wav file.  Error if not found. */\r
5605     f = fopen(ms->name, "rb");\r
5606     if (f == NULL) break;\r
5607     if (fstat(fileno(f), &st) < 0) break;\r
5608     ms->data = malloc(st.st_size);\r
5609     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5610     fclose(f);\r
5611     ok = TRUE;\r
5612     break;\r
5613   }\r
5614   if (!ok) {\r
5615     char buf[MSG_SIZ];\r
5616       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5617     DisplayError(buf, GetLastError());\r
5618   }\r
5619   return ok;\r
5620 }\r
5621 \r
5622 BOOLEAN\r
5623 MyPlaySound(MySound *ms)\r
5624 {\r
5625   BOOLEAN ok = FALSE;\r
5626 \r
5627   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5628   switch (ms->name[0]) {\r
5629   case NULLCHAR:\r
5630         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5631     /* Silence */\r
5632     ok = TRUE;\r
5633     break;\r
5634   case '$':\r
5635     /* System sound from Control Panel (deprecated feature).\r
5636        "$" alone or an unset sound name gets default beep (still in use). */\r
5637     if (ms->name[1]) {\r
5638       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5639     }\r
5640     if (!ok) ok = MessageBeep(MB_OK);\r
5641     break; \r
5642   case '!':\r
5643     /* Builtin wave resource, or "!" alone for silence */\r
5644     if (ms->name[1]) {\r
5645       if (ms->data == NULL) return FALSE;\r
5646       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5647     } else {\r
5648       ok = TRUE;\r
5649     }\r
5650     break;\r
5651   default:\r
5652     /* .wav file.  Error if not found. */\r
5653     if (ms->data == NULL) return FALSE;\r
5654     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5655     break;\r
5656   }\r
5657   /* Don't print an error: this can happen innocently if the sound driver\r
5658      is busy; for instance, if another instance of WinBoard is playing\r
5659      a sound at about the same time. */\r
5660   return ok;\r
5661 }\r
5662 \r
5663 \r
5664 LRESULT CALLBACK\r
5665 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5666 {\r
5667   BOOL ok;\r
5668   OPENFILENAME *ofn;\r
5669   static UINT *number; /* gross that this is static */\r
5670 \r
5671   switch (message) {\r
5672   case WM_INITDIALOG: /* message: initialize dialog box */\r
5673     /* Center the dialog over the application window */\r
5674     ofn = (OPENFILENAME *) lParam;\r
5675     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5676       number = (UINT *) ofn->lCustData;\r
5677       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5678     } else {\r
5679       number = NULL;\r
5680     }\r
5681     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5682     Translate(hDlg, 1536);\r
5683     return FALSE;  /* Allow for further processing */\r
5684 \r
5685   case WM_COMMAND:\r
5686     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5687       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5688     }\r
5689     return FALSE;  /* Allow for further processing */\r
5690   }\r
5691   return FALSE;\r
5692 }\r
5693 \r
5694 UINT APIENTRY\r
5695 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5696 {\r
5697   static UINT *number;\r
5698   OPENFILENAME *ofname;\r
5699   OFNOTIFY *ofnot;\r
5700   switch (uiMsg) {\r
5701   case WM_INITDIALOG:\r
5702     Translate(hdlg, DLG_IndexNumber);\r
5703     ofname = (OPENFILENAME *)lParam;\r
5704     number = (UINT *)(ofname->lCustData);\r
5705     break;\r
5706   case WM_NOTIFY:\r
5707     ofnot = (OFNOTIFY *)lParam;\r
5708     if (ofnot->hdr.code == CDN_FILEOK) {\r
5709       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5710     }\r
5711     break;\r
5712   }\r
5713   return 0;\r
5714 }\r
5715 \r
5716 \r
5717 FILE *\r
5718 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5719                char *nameFilt, char *dlgTitle, UINT *number,\r
5720                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5721 {\r
5722   OPENFILENAME openFileName;\r
5723   char buf1[MSG_SIZ];\r
5724   FILE *f;\r
5725 \r
5726   if (fileName == NULL) fileName = buf1;\r
5727   if (defName == NULL) {\r
5728     safeStrCpy(fileName, "*.", 3 );\r
5729     strcat(fileName, defExt);\r
5730   } else {\r
5731     safeStrCpy(fileName, defName, MSG_SIZ );\r
5732   }\r
5733     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5734   if (number) *number = 0;\r
5735 \r
5736   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5737   openFileName.hwndOwner         = hwnd;\r
5738   openFileName.hInstance         = (HANDLE) hInst;\r
5739   openFileName.lpstrFilter       = nameFilt;\r
5740   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5741   openFileName.nMaxCustFilter    = 0L;\r
5742   openFileName.nFilterIndex      = 1L;\r
5743   openFileName.lpstrFile         = fileName;\r
5744   openFileName.nMaxFile          = MSG_SIZ;\r
5745   openFileName.lpstrFileTitle    = fileTitle;\r
5746   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5747   openFileName.lpstrInitialDir   = NULL;\r
5748   openFileName.lpstrTitle        = dlgTitle;\r
5749   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5750     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5751     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5752     | (oldDialog ? 0 : OFN_EXPLORER);\r
5753   openFileName.nFileOffset       = 0;\r
5754   openFileName.nFileExtension    = 0;\r
5755   openFileName.lpstrDefExt       = defExt;\r
5756   openFileName.lCustData         = (LONG) number;\r
5757   openFileName.lpfnHook          = oldDialog ?\r
5758     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5759   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5760 \r
5761   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5762                         GetOpenFileName(&openFileName)) {\r
5763     /* open the file */\r
5764     f = fopen(openFileName.lpstrFile, write);\r
5765     if (f == NULL) {\r
5766       MessageBox(hwnd, _("File open failed"), NULL,\r
5767                  MB_OK|MB_ICONEXCLAMATION);\r
5768       return NULL;\r
5769     }\r
5770   } else {\r
5771     int err = CommDlgExtendedError();\r
5772     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5773     return FALSE;\r
5774   }\r
5775   return f;\r
5776 }\r
5777 \r
5778 \r
5779 \r
5780 VOID APIENTRY\r
5781 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5782 {\r
5783   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5784 \r
5785   /*\r
5786    * Get the first pop-up menu in the menu template. This is the\r
5787    * menu that TrackPopupMenu displays.\r
5788    */\r
5789   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5790 \r
5791   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5792 \r
5793   /*\r
5794    * TrackPopup uses screen coordinates, so convert the\r
5795    * coordinates of the mouse click to screen coordinates.\r
5796    */\r
5797   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5798 \r
5799   /* Draw and track the floating pop-up menu. */\r
5800   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5801                  pt.x, pt.y, 0, hwnd, NULL);\r
5802 \r
5803   /* Destroy the menu.*/\r
5804   DestroyMenu(hmenu);\r
5805 }\r
5806    \r
5807 typedef struct {\r
5808   HWND hDlg, hText;\r
5809   int sizeX, sizeY, newSizeX, newSizeY;\r
5810   HDWP hdwp;\r
5811 } ResizeEditPlusButtonsClosure;\r
5812 \r
5813 BOOL CALLBACK\r
5814 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5815 {\r
5816   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5817   RECT rect;\r
5818   POINT pt;\r
5819 \r
5820   if (hChild == cl->hText) return TRUE;\r
5821   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5822   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5823   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5824   ScreenToClient(cl->hDlg, &pt);\r
5825   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5826     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5827   return TRUE;\r
5828 }\r
5829 \r
5830 /* Resize a dialog that has a (rich) edit field filling most of\r
5831    the top, with a row of buttons below */\r
5832 VOID\r
5833 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5834 {\r
5835   RECT rectText;\r
5836   int newTextHeight, newTextWidth;\r
5837   ResizeEditPlusButtonsClosure cl;\r
5838   \r
5839   /*if (IsIconic(hDlg)) return;*/\r
5840   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5841   \r
5842   cl.hdwp = BeginDeferWindowPos(8);\r
5843 \r
5844   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5845   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5846   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5847   if (newTextHeight < 0) {\r
5848     newSizeY += -newTextHeight;\r
5849     newTextHeight = 0;\r
5850   }\r
5851   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5852     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5853 \r
5854   cl.hDlg = hDlg;\r
5855   cl.hText = hText;\r
5856   cl.sizeX = sizeX;\r
5857   cl.sizeY = sizeY;\r
5858   cl.newSizeX = newSizeX;\r
5859   cl.newSizeY = newSizeY;\r
5860   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5861 \r
5862   EndDeferWindowPos(cl.hdwp);\r
5863 }\r
5864 \r
5865 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5866 {\r
5867     RECT    rChild, rParent;\r
5868     int     wChild, hChild, wParent, hParent;\r
5869     int     wScreen, hScreen, xNew, yNew;\r
5870     HDC     hdc;\r
5871 \r
5872     /* Get the Height and Width of the child window */\r
5873     GetWindowRect (hwndChild, &rChild);\r
5874     wChild = rChild.right - rChild.left;\r
5875     hChild = rChild.bottom - rChild.top;\r
5876 \r
5877     /* Get the Height and Width of the parent window */\r
5878     GetWindowRect (hwndParent, &rParent);\r
5879     wParent = rParent.right - rParent.left;\r
5880     hParent = rParent.bottom - rParent.top;\r
5881 \r
5882     /* Get the display limits */\r
5883     hdc = GetDC (hwndChild);\r
5884     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5885     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5886     ReleaseDC(hwndChild, hdc);\r
5887 \r
5888     /* Calculate new X position, then adjust for screen */\r
5889     xNew = rParent.left + ((wParent - wChild) /2);\r
5890     if (xNew < 0) {\r
5891         xNew = 0;\r
5892     } else if ((xNew+wChild) > wScreen) {\r
5893         xNew = wScreen - wChild;\r
5894     }\r
5895 \r
5896     /* Calculate new Y position, then adjust for screen */\r
5897     if( mode == 0 ) {\r
5898         yNew = rParent.top  + ((hParent - hChild) /2);\r
5899     }\r
5900     else {\r
5901         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5902     }\r
5903 \r
5904     if (yNew < 0) {\r
5905         yNew = 0;\r
5906     } else if ((yNew+hChild) > hScreen) {\r
5907         yNew = hScreen - hChild;\r
5908     }\r
5909 \r
5910     /* Set it, and return */\r
5911     return SetWindowPos (hwndChild, NULL,\r
5912                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5913 }\r
5914 \r
5915 /* Center one window over another */\r
5916 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5917 {\r
5918     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5919 }\r
5920 \r
5921 /*---------------------------------------------------------------------------*\\r
5922  *\r
5923  * Startup Dialog functions\r
5924  *\r
5925 \*---------------------------------------------------------------------------*/\r
5926 void\r
5927 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5928 {\r
5929   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5930 \r
5931   while (*cd != NULL) {\r
5932     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5933     cd++;\r
5934   }\r
5935 }\r
5936 \r
5937 void\r
5938 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5939 {\r
5940   char buf1[MAX_ARG_LEN];\r
5941   int len;\r
5942 \r
5943   if (str[0] == '@') {\r
5944     FILE* f = fopen(str + 1, "r");\r
5945     if (f == NULL) {\r
5946       DisplayFatalError(str + 1, errno, 2);\r
5947       return;\r
5948     }\r
5949     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5950     fclose(f);\r
5951     buf1[len] = NULLCHAR;\r
5952     str = buf1;\r
5953   }\r
5954 \r
5955   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5956 \r
5957   for (;;) {\r
5958     char buf[MSG_SIZ];\r
5959     char *end = strchr(str, '\n');\r
5960     if (end == NULL) return;\r
5961     memcpy(buf, str, end - str);\r
5962     buf[end - str] = NULLCHAR;\r
5963     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5964     str = end + 1;\r
5965   }\r
5966 }\r
5967 \r
5968 void\r
5969 SetStartupDialogEnables(HWND hDlg)\r
5970 {\r
5971   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5972     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5973     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5974   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5975     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5976   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5977     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5978   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5979     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5980   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5981     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5982     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5983     IsDlgButtonChecked(hDlg, OPT_View));\r
5984 }\r
5985 \r
5986 char *\r
5987 QuoteForFilename(char *filename)\r
5988 {\r
5989   int dquote, space;\r
5990   dquote = strchr(filename, '"') != NULL;\r
5991   space = strchr(filename, ' ') != NULL;\r
5992   if (dquote || space) {\r
5993     if (dquote) {\r
5994       return "'";\r
5995     } else {\r
5996       return "\"";\r
5997     }\r
5998   } else {\r
5999     return "";\r
6000   }\r
6001 }\r
6002 \r
6003 VOID\r
6004 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6005 {\r
6006   char buf[MSG_SIZ];\r
6007   char *q;\r
6008 \r
6009   InitComboStringsFromOption(hwndCombo, nthnames);\r
6010   q = QuoteForFilename(nthcp);\r
6011     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6012   if (*nthdir != NULLCHAR) {\r
6013     q = QuoteForFilename(nthdir);\r
6014       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6015   }\r
6016   if (*nthcp == NULLCHAR) {\r
6017     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6018   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6019     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6020     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6021   }\r
6022 }\r
6023 \r
6024 LRESULT CALLBACK\r
6025 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6026 {\r
6027   char buf[MSG_SIZ];\r
6028   HANDLE hwndCombo;\r
6029   char *p;\r
6030 \r
6031   switch (message) {\r
6032   case WM_INITDIALOG:\r
6033     /* Center the dialog */\r
6034     CenterWindow (hDlg, GetDesktopWindow());\r
6035     Translate(hDlg, DLG_Startup);\r
6036     /* Initialize the dialog items */\r
6037     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6038                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6039                   firstChessProgramNames);\r
6040     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6041                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6042                   secondChessProgramNames);\r
6043     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6044     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6045       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6046     if (*appData.icsHelper != NULLCHAR) {\r
6047       char *q = QuoteForFilename(appData.icsHelper);\r
6048       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6049     }\r
6050     if (*appData.icsHost == NULLCHAR) {\r
6051       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6052       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6053     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6054       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6055       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6056     }\r
6057 \r
6058     if (appData.icsActive) {\r
6059       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6060     }\r
6061     else if (appData.noChessProgram) {\r
6062       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6063     }\r
6064     else {\r
6065       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6066     }\r
6067 \r
6068     SetStartupDialogEnables(hDlg);\r
6069     return TRUE;\r
6070 \r
6071   case WM_COMMAND:\r
6072     switch (LOWORD(wParam)) {\r
6073     case IDOK:\r
6074       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6075         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6076         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6077         p = buf;\r
6078         ParseArgs(StringGet, &p);\r
6079         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6080         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6081         p = buf;\r
6082         ParseArgs(StringGet, &p);\r
6083         appData.noChessProgram = FALSE;\r
6084         appData.icsActive = FALSE;\r
6085       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6086         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6087         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6088         p = buf;\r
6089         ParseArgs(StringGet, &p);\r
6090         if (appData.zippyPlay) {\r
6091           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6092           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6093           p = buf;\r
6094           ParseArgs(StringGet, &p);\r
6095         }\r
6096       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6097         appData.noChessProgram = TRUE;\r
6098         appData.icsActive = FALSE;\r
6099       } else {\r
6100         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6101                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6102         return TRUE;\r
6103       }\r
6104       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6105         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6106         p = buf;\r
6107         ParseArgs(StringGet, &p);\r
6108       }\r
6109       EndDialog(hDlg, TRUE);\r
6110       return TRUE;\r
6111 \r
6112     case IDCANCEL:\r
6113       ExitEvent(0);\r
6114       return TRUE;\r
6115 \r
6116     case IDM_HELPCONTENTS:\r
6117       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6118         MessageBox (GetFocus(),\r
6119                     _("Unable to activate help"),\r
6120                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6121       }\r
6122       break;\r
6123 \r
6124     default:\r
6125       SetStartupDialogEnables(hDlg);\r
6126       break;\r
6127     }\r
6128     break;\r
6129   }\r
6130   return FALSE;\r
6131 }\r
6132 \r
6133 /*---------------------------------------------------------------------------*\\r
6134  *\r
6135  * About box dialog functions\r
6136  *\r
6137 \*---------------------------------------------------------------------------*/\r
6138 \r
6139 /* Process messages for "About" dialog box */\r
6140 LRESULT CALLBACK\r
6141 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6142 {\r
6143   switch (message) {\r
6144   case WM_INITDIALOG: /* message: initialize dialog box */\r
6145     /* Center the dialog over the application window */\r
6146     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6147     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6148     Translate(hDlg, ABOUTBOX);\r
6149     JAWS_COPYRIGHT\r
6150     return (TRUE);\r
6151 \r
6152   case WM_COMMAND: /* message: received a command */\r
6153     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6154         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6155       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6156       return (TRUE);\r
6157     }\r
6158     break;\r
6159   }\r
6160   return (FALSE);\r
6161 }\r
6162 \r
6163 /*---------------------------------------------------------------------------*\\r
6164  *\r
6165  * Comment Dialog functions\r
6166  *\r
6167 \*---------------------------------------------------------------------------*/\r
6168 \r
6169 LRESULT CALLBACK\r
6170 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6171 {\r
6172   static HANDLE hwndText = NULL;\r
6173   int len, newSizeX, newSizeY, flags;\r
6174   static int sizeX, sizeY;\r
6175   char *str;\r
6176   RECT rect;\r
6177   MINMAXINFO *mmi;\r
6178 \r
6179   switch (message) {\r
6180   case WM_INITDIALOG: /* message: initialize dialog box */\r
6181     /* Initialize the dialog items */\r
6182     Translate(hDlg, DLG_EditComment);\r
6183     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6184     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6185     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6186     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6187     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6188     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6189     SetWindowText(hDlg, commentTitle);\r
6190     if (editComment) {\r
6191       SetFocus(hwndText);\r
6192     } else {\r
6193       SetFocus(GetDlgItem(hDlg, IDOK));\r
6194     }\r
6195     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6196                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6197                 MAKELPARAM(FALSE, 0));\r
6198     /* Size and position the dialog */\r
6199     if (!commentDialog) {\r
6200       commentDialog = hDlg;\r
6201       flags = SWP_NOZORDER;\r
6202       GetClientRect(hDlg, &rect);\r
6203       sizeX = rect.right;\r
6204       sizeY = rect.bottom;\r
6205       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6206           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6207         WINDOWPLACEMENT wp;\r
6208         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6209         wp.length = sizeof(WINDOWPLACEMENT);\r
6210         wp.flags = 0;\r
6211         wp.showCmd = SW_SHOW;\r
6212         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6213         wp.rcNormalPosition.left = wpComment.x;\r
6214         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6215         wp.rcNormalPosition.top = wpComment.y;\r
6216         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6217         SetWindowPlacement(hDlg, &wp);\r
6218 \r
6219         GetClientRect(hDlg, &rect);\r
6220         newSizeX = rect.right;\r
6221         newSizeY = rect.bottom;\r
6222         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6223                               newSizeX, newSizeY);\r
6224         sizeX = newSizeX;\r
6225         sizeY = newSizeY;\r
6226       }\r
6227     }\r
6228     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6229     return FALSE;\r
6230 \r
6231   case WM_COMMAND: /* message: received a command */\r
6232     switch (LOWORD(wParam)) {\r
6233     case IDOK:\r
6234       if (editComment) {\r
6235         char *p, *q;\r
6236         /* Read changed options from the dialog box */\r
6237         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6238         len = GetWindowTextLength(hwndText);\r
6239         str = (char *) malloc(len + 1);\r
6240         GetWindowText(hwndText, str, len + 1);\r
6241         p = q = str;\r
6242         while (*q) {\r
6243           if (*q == '\r')\r
6244             q++;\r
6245           else\r
6246             *p++ = *q++;\r
6247         }\r
6248         *p = NULLCHAR;\r
6249         ReplaceComment(commentIndex, str);\r
6250         free(str);\r
6251       }\r
6252       CommentPopDown();\r
6253       return TRUE;\r
6254 \r
6255     case IDCANCEL:\r
6256     case OPT_CancelComment:\r
6257       CommentPopDown();\r
6258       return TRUE;\r
6259 \r
6260     case OPT_ClearComment:\r
6261       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6262       break;\r
6263 \r
6264     case OPT_EditComment:\r
6265       EditCommentEvent();\r
6266       return TRUE;\r
6267 \r
6268     default:\r
6269       break;\r
6270     }\r
6271     break;\r
6272 \r
6273   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6274         if( wParam == OPT_CommentText ) {\r
6275             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6276 \r
6277             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6278                 POINTL pt;\r
6279                 LRESULT index;\r
6280 \r
6281                 pt.x = LOWORD( lpMF->lParam );\r
6282                 pt.y = HIWORD( lpMF->lParam );\r
6283 \r
6284                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6285 \r
6286                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6287                 len = GetWindowTextLength(hwndText);\r
6288                 str = (char *) malloc(len + 1);\r
6289                 GetWindowText(hwndText, str, len + 1);\r
6290                 ReplaceComment(commentIndex, str);\r
6291                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6292                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6293                 free(str);\r
6294 \r
6295                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6296                 lpMF->msg = WM_USER;\r
6297 \r
6298                 return TRUE;\r
6299             }\r
6300         }\r
6301         break;\r
6302 \r
6303   case WM_SIZE:\r
6304     newSizeX = LOWORD(lParam);\r
6305     newSizeY = HIWORD(lParam);\r
6306     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6307     sizeX = newSizeX;\r
6308     sizeY = newSizeY;\r
6309     break;\r
6310 \r
6311   case WM_GETMINMAXINFO:\r
6312     /* Prevent resizing window too small */\r
6313     mmi = (MINMAXINFO *) lParam;\r
6314     mmi->ptMinTrackSize.x = 100;\r
6315     mmi->ptMinTrackSize.y = 100;\r
6316     break;\r
6317   }\r
6318   return FALSE;\r
6319 }\r
6320 \r
6321 VOID\r
6322 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6323 {\r
6324   FARPROC lpProc;\r
6325   char *p, *q;\r
6326 \r
6327   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6328 \r
6329   if (str == NULL) str = "";\r
6330   p = (char *) malloc(2 * strlen(str) + 2);\r
6331   q = p;\r
6332   while (*str) {\r
6333     if (*str == '\n') *q++ = '\r';\r
6334     *q++ = *str++;\r
6335   }\r
6336   *q = NULLCHAR;\r
6337   if (commentText != NULL) free(commentText);\r
6338 \r
6339   commentIndex = index;\r
6340   commentTitle = title;\r
6341   commentText = p;\r
6342   editComment = edit;\r
6343 \r
6344   if (commentDialog) {\r
6345     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6346     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6347   } else {\r
6348     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6349     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6350                  hwndMain, (DLGPROC)lpProc);\r
6351     FreeProcInstance(lpProc);\r
6352   }\r
6353   commentUp = TRUE;\r
6354 }\r
6355 \r
6356 \r
6357 /*---------------------------------------------------------------------------*\\r
6358  *\r
6359  * Type-in move dialog functions\r
6360  * \r
6361 \*---------------------------------------------------------------------------*/\r
6362 \r
6363 LRESULT CALLBACK\r
6364 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6365 {\r
6366   char move[MSG_SIZ];\r
6367   HWND hInput;\r
6368   ChessMove moveType;\r
6369   int fromX, fromY, toX, toY;\r
6370   char promoChar;\r
6371 \r
6372   switch (message) {\r
6373   case WM_INITDIALOG:\r
6374     move[0] = (char) lParam;\r
6375     move[1] = NULLCHAR;\r
6376     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6377     Translate(hDlg, DLG_TypeInMove);\r
6378     hInput = GetDlgItem(hDlg, OPT_Move);\r
6379     SetWindowText(hInput, move);\r
6380     SetFocus(hInput);\r
6381     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6382     return FALSE;\r
6383 \r
6384   case WM_COMMAND:\r
6385     switch (LOWORD(wParam)) {\r
6386     case IDOK:
6387       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6388       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6389       { int n; Board board;\r
6390         // [HGM] FENedit\r
6391         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6392                 EditPositionPasteFEN(move);\r
6393                 EndDialog(hDlg, TRUE);\r
6394                 return TRUE;\r
6395         }\r
6396         // [HGM] movenum: allow move number to be typed in any mode\r
6397         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6398           ToNrEvent(2*n-1);\r
6399           EndDialog(hDlg, TRUE);\r
6400           return TRUE;\r
6401         }\r
6402       }\r
6403       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6404         gameMode != Training) {\r
6405         DisplayMoveError(_("Displayed move is not current"));\r
6406       } else {\r
6407 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6408         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6409           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6410         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6411         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6412           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6413           if (gameMode != Training)\r
6414               forwardMostMove = currentMove;\r
6415           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6416         } else {\r
6417           DisplayMoveError(_("Could not parse move"));\r
6418         }\r
6419       }\r
6420       EndDialog(hDlg, TRUE);\r
6421       return TRUE;\r
6422     case IDCANCEL:\r
6423       EndDialog(hDlg, FALSE);\r
6424       return TRUE;\r
6425     default:\r
6426       break;\r
6427     }\r
6428     break;\r
6429   }\r
6430   return FALSE;\r
6431 }\r
6432 \r
6433 VOID\r
6434 PopUpMoveDialog(char firstchar)\r
6435 {\r
6436     FARPROC lpProc;\r
6437     \r
6438     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6439         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6440         gameMode == AnalyzeMode || gameMode == EditGame || \r
6441         gameMode == EditPosition || gameMode == IcsExamining ||\r
6442         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6443         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6444                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6445                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6446         gameMode == Training) {\r
6447       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6448       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6449         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6450       FreeProcInstance(lpProc);\r
6451     }\r
6452 }\r
6453 \r
6454 /*---------------------------------------------------------------------------*\\r
6455  *\r
6456  * Type-in name dialog functions\r
6457  * \r
6458 \*---------------------------------------------------------------------------*/\r
6459 \r
6460 LRESULT CALLBACK\r
6461 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6462 {\r
6463   char move[MSG_SIZ];\r
6464   HWND hInput;\r
6465 \r
6466   switch (message) {\r
6467   case WM_INITDIALOG:\r
6468     move[0] = (char) lParam;\r
6469     move[1] = NULLCHAR;\r
6470     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6471     Translate(hDlg, DLG_TypeInName);\r
6472     hInput = GetDlgItem(hDlg, OPT_Name);\r
6473     SetWindowText(hInput, move);\r
6474     SetFocus(hInput);\r
6475     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6476     return FALSE;\r
6477 \r
6478   case WM_COMMAND:\r
6479     switch (LOWORD(wParam)) {\r
6480     case IDOK:\r
6481       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6482       appData.userName = strdup(move);\r
6483       SetUserLogo();\r
6484       SetGameInfo();\r
6485       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6486         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6487         DisplayTitle(move);\r
6488       }\r
6489 \r
6490 \r
6491       EndDialog(hDlg, TRUE);\r
6492       return TRUE;\r
6493     case IDCANCEL:\r
6494       EndDialog(hDlg, FALSE);\r
6495       return TRUE;\r
6496     default:\r
6497       break;\r
6498     }\r
6499     break;\r
6500   }\r
6501   return FALSE;\r
6502 }\r
6503 \r
6504 VOID\r
6505 PopUpNameDialog(char firstchar)\r
6506 {\r
6507     FARPROC lpProc;\r
6508     \r
6509       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6510       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6511         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6512       FreeProcInstance(lpProc);\r
6513 }\r
6514 \r
6515 /*---------------------------------------------------------------------------*\\r
6516  *\r
6517  *  Error dialogs\r
6518  * \r
6519 \*---------------------------------------------------------------------------*/\r
6520 \r
6521 /* Nonmodal error box */\r
6522 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6523                              WPARAM wParam, LPARAM lParam);\r
6524 \r
6525 VOID\r
6526 ErrorPopUp(char *title, char *content)\r
6527 {\r
6528   FARPROC lpProc;\r
6529   char *p, *q;\r
6530   BOOLEAN modal = hwndMain == NULL;\r
6531 \r
6532   p = content;\r
6533   q = errorMessage;\r
6534   while (*p) {\r
6535     if (*p == '\n') {\r
6536       if (modal) {\r
6537         *q++ = ' ';\r
6538         p++;\r
6539       } else {\r
6540         *q++ = '\r';\r
6541         *q++ = *p++;\r
6542       }\r
6543     } else {\r
6544       *q++ = *p++;\r
6545     }\r
6546   }\r
6547   *q = NULLCHAR;\r
6548   strncpy(errorTitle, title, sizeof(errorTitle));\r
6549   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6550   \r
6551   if (modal) {\r
6552     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6553   } else {\r
6554     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6555     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6556                  hwndMain, (DLGPROC)lpProc);\r
6557     FreeProcInstance(lpProc);\r
6558   }\r
6559 }\r
6560 \r
6561 VOID\r
6562 ErrorPopDown()\r
6563 {\r
6564   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6565   if (errorDialog == NULL) return;\r
6566   DestroyWindow(errorDialog);\r
6567   errorDialog = NULL;\r
6568   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6569 }\r
6570 \r
6571 LRESULT CALLBACK\r
6572 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6573 {\r
6574   HANDLE hwndText;\r
6575   RECT rChild;\r
6576 \r
6577   switch (message) {\r
6578   case WM_INITDIALOG:\r
6579     GetWindowRect(hDlg, &rChild);\r
6580 \r
6581     /*\r
6582     SetWindowPos(hDlg, NULL, rChild.left,\r
6583       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6584       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6585     */\r
6586 \r
6587     /* \r
6588         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6589         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6590         and it doesn't work when you resize the dialog.\r
6591         For now, just give it a default position.\r
6592     */\r
6593     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6594     Translate(hDlg, DLG_Error);\r
6595 \r
6596     errorDialog = hDlg;\r
6597     SetWindowText(hDlg, errorTitle);\r
6598     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6599     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6600     return FALSE;\r
6601 \r
6602   case WM_COMMAND:\r
6603     switch (LOWORD(wParam)) {\r
6604     case IDOK:\r
6605     case IDCANCEL:\r
6606       if (errorDialog == hDlg) errorDialog = NULL;\r
6607       DestroyWindow(hDlg);\r
6608       return TRUE;\r
6609 \r
6610     default:\r
6611       break;\r
6612     }\r
6613     break;\r
6614   }\r
6615   return FALSE;\r
6616 }\r
6617 \r
6618 #ifdef GOTHIC\r
6619 HWND gothicDialog = NULL;\r
6620 \r
6621 LRESULT CALLBACK\r
6622 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6623 {\r
6624   HANDLE hwndText;\r
6625   RECT rChild;\r
6626   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6627 \r
6628   switch (message) {\r
6629   case WM_INITDIALOG:\r
6630     GetWindowRect(hDlg, &rChild);\r
6631 \r
6632     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6633                                                              SWP_NOZORDER);\r
6634 \r
6635     /* \r
6636         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6637         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6638         and it doesn't work when you resize the dialog.\r
6639         For now, just give it a default position.\r
6640     */\r
6641     gothicDialog = hDlg;\r
6642     SetWindowText(hDlg, errorTitle);\r
6643     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6644     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6645     return FALSE;\r
6646 \r
6647   case WM_COMMAND:\r
6648     switch (LOWORD(wParam)) {\r
6649     case IDOK:\r
6650     case IDCANCEL:\r
6651       if (errorDialog == hDlg) errorDialog = NULL;\r
6652       DestroyWindow(hDlg);\r
6653       return TRUE;\r
6654 \r
6655     default:\r
6656       break;\r
6657     }\r
6658     break;\r
6659   }\r
6660   return FALSE;\r
6661 }\r
6662 \r
6663 VOID\r
6664 GothicPopUp(char *title, VariantClass variant)\r
6665 {\r
6666   FARPROC lpProc;\r
6667   static char *lastTitle;\r
6668 \r
6669   strncpy(errorTitle, title, sizeof(errorTitle));\r
6670   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6671 \r
6672   if(lastTitle != title && gothicDialog != NULL) {\r
6673     DestroyWindow(gothicDialog);\r
6674     gothicDialog = NULL;\r
6675   }\r
6676   if(variant != VariantNormal && gothicDialog == NULL) {\r
6677     title = lastTitle;\r
6678     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6679     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6680                  hwndMain, (DLGPROC)lpProc);\r
6681     FreeProcInstance(lpProc);\r
6682   }\r
6683 }\r
6684 #endif\r
6685 \r
6686 /*---------------------------------------------------------------------------*\\r
6687  *\r
6688  *  Ics Interaction console functions\r
6689  *\r
6690 \*---------------------------------------------------------------------------*/\r
6691 \r
6692 #define HISTORY_SIZE 64\r
6693 static char *history[HISTORY_SIZE];\r
6694 int histIn = 0, histP = 0;\r
6695 \r
6696 VOID\r
6697 SaveInHistory(char *cmd)\r
6698 {\r
6699   if (history[histIn] != NULL) {\r
6700     free(history[histIn]);\r
6701     history[histIn] = NULL;\r
6702   }\r
6703   if (*cmd == NULLCHAR) return;\r
6704   history[histIn] = StrSave(cmd);\r
6705   histIn = (histIn + 1) % HISTORY_SIZE;\r
6706   if (history[histIn] != NULL) {\r
6707     free(history[histIn]);\r
6708     history[histIn] = NULL;\r
6709   }\r
6710   histP = histIn;\r
6711 }\r
6712 \r
6713 char *\r
6714 PrevInHistory(char *cmd)\r
6715 {\r
6716   int newhp;\r
6717   if (histP == histIn) {\r
6718     if (history[histIn] != NULL) free(history[histIn]);\r
6719     history[histIn] = StrSave(cmd);\r
6720   }\r
6721   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6722   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6723   histP = newhp;\r
6724   return history[histP];\r
6725 }\r
6726 \r
6727 char *\r
6728 NextInHistory()\r
6729 {\r
6730   if (histP == histIn) return NULL;\r
6731   histP = (histP + 1) % HISTORY_SIZE;\r
6732   return history[histP];   \r
6733 }\r
6734 \r
6735 HMENU\r
6736 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6737 {\r
6738   HMENU hmenu, h;\r
6739   int i = 0;\r
6740   hmenu = LoadMenu(hInst, "TextMenu");\r
6741   h = GetSubMenu(hmenu, 0);\r
6742   while (e->item) {\r
6743     if (strcmp(e->item, "-") == 0) {\r
6744       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6745     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6746       int flags = MF_STRING, j = 0;\r
6747       if (e->item[0] == '|') {\r
6748         flags |= MF_MENUBARBREAK;\r
6749         j++;\r
6750       }\r
6751       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6752       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6753     }\r
6754     e++;\r
6755     i++;\r
6756   } \r
6757   return hmenu;\r
6758 }\r
6759 \r
6760 WNDPROC consoleTextWindowProc;\r
6761 \r
6762 void\r
6763 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6764 {\r
6765   char buf[MSG_SIZ], name[MSG_SIZ];\r
6766   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6767   CHARRANGE sel;\r
6768 \r
6769   if (!getname) {\r
6770     SetWindowText(hInput, command);\r
6771     if (immediate) {\r
6772       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6773     } else {\r
6774       sel.cpMin = 999999;\r
6775       sel.cpMax = 999999;\r
6776       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6777       SetFocus(hInput);\r
6778     }\r
6779     return;\r
6780   }    \r
6781   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6782   if (sel.cpMin == sel.cpMax) {\r
6783     /* Expand to surrounding word */\r
6784     TEXTRANGE tr;\r
6785     do {\r
6786       tr.chrg.cpMax = sel.cpMin;\r
6787       tr.chrg.cpMin = --sel.cpMin;\r
6788       if (sel.cpMin < 0) break;\r
6789       tr.lpstrText = name;\r
6790       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6791     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6792     sel.cpMin++;\r
6793 \r
6794     do {\r
6795       tr.chrg.cpMin = sel.cpMax;\r
6796       tr.chrg.cpMax = ++sel.cpMax;\r
6797       tr.lpstrText = name;\r
6798       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6799     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6800     sel.cpMax--;\r
6801 \r
6802     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6803       MessageBeep(MB_ICONEXCLAMATION);\r
6804       return;\r
6805     }\r
6806     tr.chrg = sel;\r
6807     tr.lpstrText = name;\r
6808     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6809   } else {\r
6810     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6811       MessageBeep(MB_ICONEXCLAMATION);\r
6812       return;\r
6813     }\r
6814     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6815   }\r
6816   if (immediate) {\r
6817     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6818     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6819     SetWindowText(hInput, buf);\r
6820     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6821   } else {\r
6822     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6823       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6824     SetWindowText(hInput, buf);\r
6825     sel.cpMin = 999999;\r
6826     sel.cpMax = 999999;\r
6827     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6828     SetFocus(hInput);\r
6829   }\r
6830 }\r
6831 \r
6832 LRESULT CALLBACK \r
6833 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6834 {\r
6835   HWND hInput;\r
6836   CHARRANGE sel;\r
6837 \r
6838   switch (message) {\r
6839   case WM_KEYDOWN:\r
6840     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6841     switch (wParam) {\r
6842     case VK_PRIOR:\r
6843       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6844       return 0;\r
6845     case VK_NEXT:\r
6846       sel.cpMin = 999999;\r
6847       sel.cpMax = 999999;\r
6848       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6849       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6850       return 0;\r
6851     }\r
6852     break;\r
6853   case WM_CHAR:\r
6854    if(wParam != '\022') {\r
6855     if (wParam == '\t') {\r
6856       if (GetKeyState(VK_SHIFT) < 0) {\r
6857         /* shifted */\r
6858         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6859         if (buttonDesc[0].hwnd) {\r
6860           SetFocus(buttonDesc[0].hwnd);\r
6861         } else {\r
6862           SetFocus(hwndMain);\r
6863         }\r
6864       } else {\r
6865         /* unshifted */\r
6866         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6867       }\r
6868     } else {\r
6869       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6870       JAWS_DELETE( SetFocus(hInput); )\r
6871       SendMessage(hInput, message, wParam, lParam);\r
6872     }\r
6873     return 0;\r
6874    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6875   case WM_RBUTTONDOWN:\r
6876     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6877       /* Move selection here if it was empty */\r
6878       POINT pt;\r
6879       pt.x = LOWORD(lParam);\r
6880       pt.y = HIWORD(lParam);\r
6881       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6882       if (sel.cpMin == sel.cpMax) {\r
6883         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6884         sel.cpMax = sel.cpMin;\r
6885         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6886       }\r
6887       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6888 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6889       POINT pt;\r
6890       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6891       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6892       if (sel.cpMin == sel.cpMax) {\r
6893         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6894         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6895       }\r
6896       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6897         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6898       }\r
6899       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6900       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6901       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6902       MenuPopup(hwnd, pt, hmenu, -1);\r
6903 }\r
6904     }\r
6905     return 0;\r
6906   case WM_RBUTTONUP:\r
6907     if (GetKeyState(VK_SHIFT) & ~1) {\r
6908       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6909         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6910     }\r
6911     return 0;\r
6912   case WM_PASTE:\r
6913     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6914     SetFocus(hInput);\r
6915     return SendMessage(hInput, message, wParam, lParam);\r
6916   case WM_MBUTTONDOWN:\r
6917     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6918   case WM_COMMAND:\r
6919     switch (LOWORD(wParam)) {\r
6920     case IDM_QuickPaste:\r
6921       {\r
6922         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6923         if (sel.cpMin == sel.cpMax) {\r
6924           MessageBeep(MB_ICONEXCLAMATION);\r
6925           return 0;\r
6926         }\r
6927         SendMessage(hwnd, WM_COPY, 0, 0);\r
6928         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6929         SendMessage(hInput, WM_PASTE, 0, 0);\r
6930         SetFocus(hInput);\r
6931         return 0;\r
6932       }\r
6933     case IDM_Cut:\r
6934       SendMessage(hwnd, WM_CUT, 0, 0);\r
6935       return 0;\r
6936     case IDM_Paste:\r
6937       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6938       return 0;\r
6939     case IDM_Copy:\r
6940       SendMessage(hwnd, WM_COPY, 0, 0);\r
6941       return 0;\r
6942     default:\r
6943       {\r
6944         int i = LOWORD(wParam) - IDM_CommandX;\r
6945         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6946             icsTextMenuEntry[i].command != NULL) {\r
6947           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6948                    icsTextMenuEntry[i].getname,\r
6949                    icsTextMenuEntry[i].immediate);\r
6950           return 0;\r
6951         }\r
6952       }\r
6953       break;\r
6954     }\r
6955     break;\r
6956   }\r
6957   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6958 }\r
6959 \r
6960 WNDPROC consoleInputWindowProc;\r
6961 \r
6962 LRESULT CALLBACK\r
6963 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6964 {\r
6965   char buf[MSG_SIZ];\r
6966   char *p;\r
6967   static BOOL sendNextChar = FALSE;\r
6968   static BOOL quoteNextChar = FALSE;\r
6969   InputSource *is = consoleInputSource;\r
6970   CHARFORMAT cf;\r
6971   CHARRANGE sel;\r
6972 \r
6973   switch (message) {\r
6974   case WM_CHAR:\r
6975     if (!appData.localLineEditing || sendNextChar) {\r
6976       is->buf[0] = (CHAR) wParam;\r
6977       is->count = 1;\r
6978       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6979       sendNextChar = FALSE;\r
6980       return 0;\r
6981     }\r
6982     if (quoteNextChar) {\r
6983       buf[0] = (char) wParam;\r
6984       buf[1] = NULLCHAR;\r
6985       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6986       quoteNextChar = FALSE;\r
6987       return 0;\r
6988     }\r
6989     switch (wParam) {\r
6990     case '\r':   /* Enter key */\r
6991       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6992       if (consoleEcho) SaveInHistory(is->buf);\r
6993       is->buf[is->count++] = '\n';\r
6994       is->buf[is->count] = NULLCHAR;\r
6995       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6996       if (consoleEcho) {\r
6997         ConsoleOutput(is->buf, is->count, TRUE);\r
6998       } else if (appData.localLineEditing) {\r
6999         ConsoleOutput("\n", 1, TRUE);\r
7000       }\r
7001       /* fall thru */\r
7002     case '\033': /* Escape key */\r
7003       SetWindowText(hwnd, "");\r
7004       cf.cbSize = sizeof(CHARFORMAT);\r
7005       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7006       if (consoleEcho) {\r
7007         cf.crTextColor = textAttribs[ColorNormal].color;\r
7008       } else {\r
7009         cf.crTextColor = COLOR_ECHOOFF;\r
7010       }\r
7011       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7012       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7013       return 0;\r
7014     case '\t':   /* Tab key */\r
7015       if (GetKeyState(VK_SHIFT) < 0) {\r
7016         /* shifted */\r
7017         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7018       } else {\r
7019         /* unshifted */\r
7020         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7021         if (buttonDesc[0].hwnd) {\r
7022           SetFocus(buttonDesc[0].hwnd);\r
7023         } else {\r
7024           SetFocus(hwndMain);\r
7025         }\r
7026       }\r
7027       return 0;\r
7028     case '\023': /* Ctrl+S */\r
7029       sendNextChar = TRUE;\r
7030       return 0;\r
7031     case '\021': /* Ctrl+Q */\r
7032       quoteNextChar = TRUE;\r
7033       return 0;\r
7034     JAWS_REPLAY\r
7035     default:\r
7036       break;\r
7037     }\r
7038     break;\r
7039   case WM_KEYDOWN:\r
7040     switch (wParam) {\r
7041     case VK_UP:\r
7042       GetWindowText(hwnd, buf, MSG_SIZ);\r
7043       p = PrevInHistory(buf);\r
7044       if (p != NULL) {\r
7045         SetWindowText(hwnd, p);\r
7046         sel.cpMin = 999999;\r
7047         sel.cpMax = 999999;\r
7048         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7049         return 0;\r
7050       }\r
7051       break;\r
7052     case VK_DOWN:\r
7053       p = NextInHistory();\r
7054       if (p != NULL) {\r
7055         SetWindowText(hwnd, p);\r
7056         sel.cpMin = 999999;\r
7057         sel.cpMax = 999999;\r
7058         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7059         return 0;\r
7060       }\r
7061       break;\r
7062     case VK_HOME:\r
7063     case VK_END:\r
7064       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7065       /* fall thru */\r
7066     case VK_PRIOR:\r
7067     case VK_NEXT:\r
7068       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7069       return 0;\r
7070     }\r
7071     break;\r
7072   case WM_MBUTTONDOWN:\r
7073     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7074       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7075     break;\r
7076   case WM_RBUTTONUP:\r
7077     if (GetKeyState(VK_SHIFT) & ~1) {\r
7078       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7079         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7080     } else {\r
7081       POINT pt;\r
7082       HMENU hmenu;\r
7083       hmenu = LoadMenu(hInst, "InputMenu");\r
7084       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7085       if (sel.cpMin == sel.cpMax) {\r
7086         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7087         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7088       }\r
7089       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7090         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7091       }\r
7092       pt.x = LOWORD(lParam);\r
7093       pt.y = HIWORD(lParam);\r
7094       MenuPopup(hwnd, pt, hmenu, -1);\r
7095     }\r
7096     return 0;\r
7097   case WM_COMMAND:\r
7098     switch (LOWORD(wParam)) { \r
7099     case IDM_Undo:\r
7100       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7101       return 0;\r
7102     case IDM_SelectAll:\r
7103       sel.cpMin = 0;\r
7104       sel.cpMax = -1; /*999999?*/\r
7105       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7106       return 0;\r
7107     case IDM_Cut:\r
7108       SendMessage(hwnd, WM_CUT, 0, 0);\r
7109       return 0;\r
7110     case IDM_Paste:\r
7111       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7112       return 0;\r
7113     case IDM_Copy:\r
7114       SendMessage(hwnd, WM_COPY, 0, 0);\r
7115       return 0;\r
7116     }\r
7117     break;\r
7118   }\r
7119   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7120 }\r
7121 \r
7122 #define CO_MAX  100000\r
7123 #define CO_TRIM   1000\r
7124 \r
7125 LRESULT CALLBACK\r
7126 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7127 {\r
7128   static SnapData sd;\r
7129   HWND hText, hInput;\r
7130   RECT rect;\r
7131   static int sizeX, sizeY;\r
7132   int newSizeX, newSizeY;\r
7133   MINMAXINFO *mmi;\r
7134   WORD wMask;\r
7135 \r
7136   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7137   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7138 \r
7139   switch (message) {\r
7140   case WM_NOTIFY:\r
7141     if (((NMHDR*)lParam)->code == EN_LINK)\r
7142     {\r
7143       ENLINK *pLink = (ENLINK*)lParam;\r
7144       if (pLink->msg == WM_LBUTTONUP)\r
7145       {\r
7146         TEXTRANGE tr;\r
7147 \r
7148         tr.chrg = pLink->chrg;\r
7149         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7150         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7151         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7152         free(tr.lpstrText);\r
7153       }\r
7154     }\r
7155     break;\r
7156   case WM_INITDIALOG: /* message: initialize dialog box */\r
7157     hwndConsole = hDlg;\r
7158     SetFocus(hInput);\r
7159     consoleTextWindowProc = (WNDPROC)\r
7160       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7161     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7162     consoleInputWindowProc = (WNDPROC)\r
7163       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7164     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7165     Colorize(ColorNormal, TRUE);\r
7166     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7167     ChangedConsoleFont();\r
7168     GetClientRect(hDlg, &rect);\r
7169     sizeX = rect.right;\r
7170     sizeY = rect.bottom;\r
7171     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7172         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7173       WINDOWPLACEMENT wp;\r
7174       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7175       wp.length = sizeof(WINDOWPLACEMENT);\r
7176       wp.flags = 0;\r
7177       wp.showCmd = SW_SHOW;\r
7178       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7179       wp.rcNormalPosition.left = wpConsole.x;\r
7180       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7181       wp.rcNormalPosition.top = wpConsole.y;\r
7182       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7183       SetWindowPlacement(hDlg, &wp);\r
7184     }\r
7185 \r
7186    // [HGM] Chessknight's change 2004-07-13\r
7187    else { /* Determine Defaults */\r
7188        WINDOWPLACEMENT wp;\r
7189        wpConsole.x = wpMain.width + 1;\r
7190        wpConsole.y = wpMain.y;\r
7191        wpConsole.width = screenWidth -  wpMain.width;\r
7192        wpConsole.height = wpMain.height;\r
7193        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7194        wp.length = sizeof(WINDOWPLACEMENT);\r
7195        wp.flags = 0;\r
7196        wp.showCmd = SW_SHOW;\r
7197        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7198        wp.rcNormalPosition.left = wpConsole.x;\r
7199        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7200        wp.rcNormalPosition.top = wpConsole.y;\r
7201        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7202        SetWindowPlacement(hDlg, &wp);\r
7203     }\r
7204 \r
7205    // Allow hText to highlight URLs and send notifications on them\r
7206    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7207    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7208    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7209    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7210 \r
7211     return FALSE;\r
7212 \r
7213   case WM_SETFOCUS:\r
7214     SetFocus(hInput);\r
7215     return 0;\r
7216 \r
7217   case WM_CLOSE:\r
7218     ExitEvent(0);\r
7219     /* not reached */\r
7220     break;\r
7221 \r
7222   case WM_SIZE:\r
7223     if (IsIconic(hDlg)) break;\r
7224     newSizeX = LOWORD(lParam);\r
7225     newSizeY = HIWORD(lParam);\r
7226     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7227       RECT rectText, rectInput;\r
7228       POINT pt;\r
7229       int newTextHeight, newTextWidth;\r
7230       GetWindowRect(hText, &rectText);\r
7231       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7232       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7233       if (newTextHeight < 0) {\r
7234         newSizeY += -newTextHeight;\r
7235         newTextHeight = 0;\r
7236       }\r
7237       SetWindowPos(hText, NULL, 0, 0,\r
7238         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7239       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7240       pt.x = rectInput.left;\r
7241       pt.y = rectInput.top + newSizeY - sizeY;\r
7242       ScreenToClient(hDlg, &pt);\r
7243       SetWindowPos(hInput, NULL, \r
7244         pt.x, pt.y, /* needs client coords */   \r
7245         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7246         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7247     }\r
7248     sizeX = newSizeX;\r
7249     sizeY = newSizeY;\r
7250     break;\r
7251 \r
7252   case WM_GETMINMAXINFO:\r
7253     /* Prevent resizing window too small */\r
7254     mmi = (MINMAXINFO *) lParam;\r
7255     mmi->ptMinTrackSize.x = 100;\r
7256     mmi->ptMinTrackSize.y = 100;\r
7257     break;\r
7258 \r
7259   /* [AS] Snapping */\r
7260   case WM_ENTERSIZEMOVE:\r
7261     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7262 \r
7263   case WM_SIZING:\r
7264     return OnSizing( &sd, hDlg, wParam, lParam );\r
7265 \r
7266   case WM_MOVING:\r
7267     return OnMoving( &sd, hDlg, wParam, lParam );\r
7268 \r
7269   case WM_EXITSIZEMOVE:\r
7270         UpdateICSWidth(hText);\r
7271     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7272   }\r
7273 \r
7274   return DefWindowProc(hDlg, message, wParam, lParam);\r
7275 }\r
7276 \r
7277 \r
7278 VOID\r
7279 ConsoleCreate()\r
7280 {\r
7281   HWND hCons;\r
7282   if (hwndConsole) return;\r
7283   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7284   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7285 }\r
7286 \r
7287 \r
7288 VOID\r
7289 ConsoleOutput(char* data, int length, int forceVisible)\r
7290 {\r
7291   HWND hText;\r
7292   int trim, exlen;\r
7293   char *p, *q;\r
7294   char buf[CO_MAX+1];\r
7295   POINT pEnd;\r
7296   RECT rect;\r
7297   static int delayLF = 0;\r
7298   CHARRANGE savesel, sel;\r
7299 \r
7300   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7301   p = data;\r
7302   q = buf;\r
7303   if (delayLF) {\r
7304     *q++ = '\r';\r
7305     *q++ = '\n';\r
7306     delayLF = 0;\r
7307   }\r
7308   while (length--) {\r
7309     if (*p == '\n') {\r
7310       if (*++p) {\r
7311         *q++ = '\r';\r
7312         *q++ = '\n';\r
7313       } else {\r
7314         delayLF = 1;\r
7315       }\r
7316     } else if (*p == '\007') {\r
7317        MyPlaySound(&sounds[(int)SoundBell]);\r
7318        p++;\r
7319     } else {\r
7320       *q++ = *p++;\r
7321     }\r
7322   }\r
7323   *q = NULLCHAR;\r
7324   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7325   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7326   /* Save current selection */\r
7327   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7328   exlen = GetWindowTextLength(hText);\r
7329   /* Find out whether current end of text is visible */\r
7330   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7331   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7332   /* Trim existing text if it's too long */\r
7333   if (exlen + (q - buf) > CO_MAX) {\r
7334     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7335     sel.cpMin = 0;\r
7336     sel.cpMax = trim;\r
7337     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7338     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7339     exlen -= trim;\r
7340     savesel.cpMin -= trim;\r
7341     savesel.cpMax -= trim;\r
7342     if (exlen < 0) exlen = 0;\r
7343     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7344     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7345   }\r
7346   /* Append the new text */\r
7347   sel.cpMin = exlen;\r
7348   sel.cpMax = exlen;\r
7349   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7350   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7351   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7352   if (forceVisible || exlen == 0 ||\r
7353       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7354        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7355     /* Scroll to make new end of text visible if old end of text\r
7356        was visible or new text is an echo of user typein */\r
7357     sel.cpMin = 9999999;\r
7358     sel.cpMax = 9999999;\r
7359     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7360     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7361     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7362     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7363   }\r
7364   if (savesel.cpMax == exlen || forceVisible) {\r
7365     /* Move insert point to new end of text if it was at the old\r
7366        end of text or if the new text is an echo of user typein */\r
7367     sel.cpMin = 9999999;\r
7368     sel.cpMax = 9999999;\r
7369     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7370   } else {\r
7371     /* Restore previous selection */\r
7372     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7373   }\r
7374   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7375 }\r
7376 \r
7377 /*---------*/\r
7378 \r
7379 \r
7380 void\r
7381 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7382 {\r
7383   char buf[100];\r
7384   char *str;\r
7385   COLORREF oldFg, oldBg;\r
7386   HFONT oldFont;\r
7387   RECT rect;\r
7388 \r
7389   if(copyNumber > 1)
7390     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7391 \r
7392   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7393   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7394   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7395 \r
7396   rect.left = x;\r
7397   rect.right = x + squareSize;\r
7398   rect.top  = y;\r
7399   rect.bottom = y + squareSize;\r
7400   str = buf;\r
7401 \r
7402   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7403                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7404              y, ETO_CLIPPED|ETO_OPAQUE,\r
7405              &rect, str, strlen(str), NULL);\r
7406 \r
7407   (void) SetTextColor(hdc, oldFg);\r
7408   (void) SetBkColor(hdc, oldBg);\r
7409   (void) SelectObject(hdc, oldFont);\r
7410 }\r
7411 \r
7412 void\r
7413 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7414               RECT *rect, char *color, char *flagFell)\r
7415 {\r
7416   char buf[100];\r
7417   char *str;\r
7418   COLORREF oldFg, oldBg;\r
7419   HFONT oldFont;\r
7420 \r
7421   if (appData.clockMode) {\r
7422     if (tinyLayout)\r
7423       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7424     else\r
7425       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7426     str = buf;\r
7427   } else {\r
7428     str = color;\r
7429   }\r
7430 \r
7431   if (highlight) {\r
7432     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7433     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7434   } else {\r
7435     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7436     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7437   }\r
7438   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7439 \r
7440   JAWS_SILENCE\r
7441 \r
7442   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7443              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7444              rect, str, strlen(str), NULL);\r
7445   if(logoHeight > 0 && appData.clockMode) {\r
7446       RECT r;\r
7447       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);\r
7448       r.top = rect->top + logoHeight/2;\r
7449       r.left = rect->left;\r
7450       r.right = rect->right;\r
7451       r.bottom = rect->bottom;\r
7452       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7453                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7454                  &r, str, strlen(str), NULL);\r
7455   }\r
7456   (void) SetTextColor(hdc, oldFg);\r
7457   (void) SetBkColor(hdc, oldBg);\r
7458   (void) SelectObject(hdc, oldFont);\r
7459 }\r
7460 \r
7461 \r
7462 int\r
7463 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7464            OVERLAPPED *ovl)\r
7465 {\r
7466   int ok, err;\r
7467 \r
7468   /* [AS]  */\r
7469   if( count <= 0 ) {\r
7470     if (appData.debugMode) {\r
7471       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7472     }\r
7473 \r
7474     return ERROR_INVALID_USER_BUFFER;\r
7475   }\r
7476 \r
7477   ResetEvent(ovl->hEvent);\r
7478   ovl->Offset = ovl->OffsetHigh = 0;\r
7479   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7480   if (ok) {\r
7481     err = NO_ERROR;\r
7482   } else {\r
7483     err = GetLastError();\r
7484     if (err == ERROR_IO_PENDING) {\r
7485       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7486       if (ok)\r
7487         err = NO_ERROR;\r
7488       else\r
7489         err = GetLastError();\r
7490     }\r
7491   }\r
7492   return err;\r
7493 }\r
7494 \r
7495 int\r
7496 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7497             OVERLAPPED *ovl)\r
7498 {\r
7499   int ok, err;\r
7500 \r
7501   ResetEvent(ovl->hEvent);\r
7502   ovl->Offset = ovl->OffsetHigh = 0;\r
7503   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7504   if (ok) {\r
7505     err = NO_ERROR;\r
7506   } else {\r
7507     err = GetLastError();\r
7508     if (err == ERROR_IO_PENDING) {\r
7509       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7510       if (ok)\r
7511         err = NO_ERROR;\r
7512       else\r
7513         err = GetLastError();\r
7514     }\r
7515   }\r
7516   return err;\r
7517 }\r
7518 \r
7519 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7520 void CheckForInputBufferFull( InputSource * is )\r
7521 {\r
7522     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7523         /* Look for end of line */\r
7524         char * p = is->buf;\r
7525         \r
7526         while( p < is->next && *p != '\n' ) {\r
7527             p++;\r
7528         }\r
7529 \r
7530         if( p >= is->next ) {\r
7531             if (appData.debugMode) {\r
7532                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7533             }\r
7534 \r
7535             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7536             is->count = (DWORD) -1;\r
7537             is->next = is->buf;\r
7538         }\r
7539     }\r
7540 }\r
7541 \r
7542 DWORD\r
7543 InputThread(LPVOID arg)\r
7544 {\r
7545   InputSource *is;\r
7546   OVERLAPPED ovl;\r
7547 \r
7548   is = (InputSource *) arg;\r
7549   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7550   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7551   while (is->hThread != NULL) {\r
7552     is->error = DoReadFile(is->hFile, is->next,\r
7553                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7554                            &is->count, &ovl);\r
7555     if (is->error == NO_ERROR) {\r
7556       is->next += is->count;\r
7557     } else {\r
7558       if (is->error == ERROR_BROKEN_PIPE) {\r
7559         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7560         is->count = 0;\r
7561       } else {\r
7562         is->count = (DWORD) -1;\r
7563         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7564         break; \r
7565       }\r
7566     }\r
7567 \r
7568     CheckForInputBufferFull( is );\r
7569 \r
7570     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7571 \r
7572     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7573 \r
7574     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7575   }\r
7576 \r
7577   CloseHandle(ovl.hEvent);\r
7578   CloseHandle(is->hFile);\r
7579 \r
7580   if (appData.debugMode) {\r
7581     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7582   }\r
7583 \r
7584   return 0;\r
7585 }\r
7586 \r
7587 \r
7588 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7589 DWORD\r
7590 NonOvlInputThread(LPVOID arg)\r
7591 {\r
7592   InputSource *is;\r
7593   char *p, *q;\r
7594   int i;\r
7595   char prev;\r
7596 \r
7597   is = (InputSource *) arg;\r
7598   while (is->hThread != NULL) {\r
7599     is->error = ReadFile(is->hFile, is->next,\r
7600                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7601                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7602     if (is->error == NO_ERROR) {\r
7603       /* Change CRLF to LF */\r
7604       if (is->next > is->buf) {\r
7605         p = is->next - 1;\r
7606         i = is->count + 1;\r
7607       } else {\r
7608         p = is->next;\r
7609         i = is->count;\r
7610       }\r
7611       q = p;\r
7612       prev = NULLCHAR;\r
7613       while (i > 0) {\r
7614         if (prev == '\r' && *p == '\n') {\r
7615           *(q-1) = '\n';\r
7616           is->count--;\r
7617         } else { \r
7618           *q++ = *p;\r
7619         }\r
7620         prev = *p++;\r
7621         i--;\r
7622       }\r
7623       *q = NULLCHAR;\r
7624       is->next = q;\r
7625     } else {\r
7626       if (is->error == ERROR_BROKEN_PIPE) {\r
7627         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7628         is->count = 0; \r
7629       } else {\r
7630         is->count = (DWORD) -1;\r
7631       }\r
7632     }\r
7633 \r
7634     CheckForInputBufferFull( is );\r
7635 \r
7636     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7637 \r
7638     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7639 \r
7640     if (is->count < 0) break;  /* Quit on error */\r
7641   }\r
7642   CloseHandle(is->hFile);\r
7643   return 0;\r
7644 }\r
7645 \r
7646 DWORD\r
7647 SocketInputThread(LPVOID arg)\r
7648 {\r
7649   InputSource *is;\r
7650 \r
7651   is = (InputSource *) arg;\r
7652   while (is->hThread != NULL) {\r
7653     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7654     if ((int)is->count == SOCKET_ERROR) {\r
7655       is->count = (DWORD) -1;\r
7656       is->error = WSAGetLastError();\r
7657     } else {\r
7658       is->error = NO_ERROR;\r
7659       is->next += is->count;\r
7660       if (is->count == 0 && is->second == is) {\r
7661         /* End of file on stderr; quit with no message */\r
7662         break;\r
7663       }\r
7664     }\r
7665     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7666 \r
7667     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7668 \r
7669     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7670   }\r
7671   return 0;\r
7672 }\r
7673 \r
7674 VOID\r
7675 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7676 {\r
7677   InputSource *is;\r
7678 \r
7679   is = (InputSource *) lParam;\r
7680   if (is->lineByLine) {\r
7681     /* Feed in lines one by one */\r
7682     char *p = is->buf;\r
7683     char *q = p;\r
7684     while (q < is->next) {\r
7685       if (*q++ == '\n') {\r
7686         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7687         p = q;\r
7688       }\r
7689     }\r
7690     \r
7691     /* Move any partial line to the start of the buffer */\r
7692     q = is->buf;\r
7693     while (p < is->next) {\r
7694       *q++ = *p++;\r
7695     }\r
7696     is->next = q;\r
7697 \r
7698     if (is->error != NO_ERROR || is->count == 0) {\r
7699       /* Notify backend of the error.  Note: If there was a partial\r
7700          line at the end, it is not flushed through. */\r
7701       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7702     }\r
7703   } else {\r
7704     /* Feed in the whole chunk of input at once */\r
7705     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7706     is->next = is->buf;\r
7707   }\r
7708 }\r
7709 \r
7710 /*---------------------------------------------------------------------------*\\r
7711  *\r
7712  *  Menu enables. Used when setting various modes.\r
7713  *\r
7714 \*---------------------------------------------------------------------------*/\r
7715 \r
7716 typedef struct {\r
7717   int item;\r
7718   int flags;\r
7719 } Enables;\r
7720 \r
7721 VOID\r
7722 GreyRevert(Boolean grey)\r
7723 { // [HGM] vari: for retracting variations in local mode\r
7724   HMENU hmenu = GetMenu(hwndMain);\r
7725   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7726   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7727 }\r
7728 \r
7729 VOID\r
7730 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7731 {\r
7732   while (enab->item > 0) {\r
7733     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7734     enab++;\r
7735   }\r
7736 }\r
7737 \r
7738 Enables gnuEnables[] = {\r
7739   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7740   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7747   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7751   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7752   { -1, -1 }\r
7753 };\r
7754 \r
7755 Enables icsEnables[] = {\r
7756   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7757   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7763   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7764   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7765   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7766   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7770   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7771   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7773   { -1, -1 }\r
7774 };\r
7775 \r
7776 #if ZIPPY\r
7777 Enables zippyEnables[] = {\r
7778   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7779   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7780   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7781   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7782   { -1, -1 }\r
7783 };\r
7784 #endif\r
7785 \r
7786 Enables ncpEnables[] = {\r
7787   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7796   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7797   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7808   { -1, -1 }\r
7809 };\r
7810 \r
7811 Enables trainingOnEnables[] = {\r
7812   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7813   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7820   { -1, -1 }\r
7821 };\r
7822 \r
7823 Enables trainingOffEnables[] = {\r
7824   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7825   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7826   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7827   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7828   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7829   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7830   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7831   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7832   { -1, -1 }\r
7833 };\r
7834 \r
7835 /* These modify either ncpEnables or gnuEnables */\r
7836 Enables cmailEnables[] = {\r
7837   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7838   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7839   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7840   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7842   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7844   { -1, -1 }\r
7845 };\r
7846 \r
7847 Enables machineThinkingEnables[] = {\r
7848   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7864   { -1, -1 }\r
7865 };\r
7866 \r
7867 Enables userThinkingEnables[] = {\r
7868   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7870   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7871   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7872   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7877   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7881   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7882   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7883   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7884   { -1, -1 }\r
7885 };\r
7886 \r
7887 /*---------------------------------------------------------------------------*\\r
7888  *\r
7889  *  Front-end interface functions exported by XBoard.\r
7890  *  Functions appear in same order as prototypes in frontend.h.\r
7891  * \r
7892 \*---------------------------------------------------------------------------*/\r
7893 VOID\r
7894 ModeHighlight()\r
7895 {\r
7896   static UINT prevChecked = 0;\r
7897   static int prevPausing = 0;\r
7898   UINT nowChecked;\r
7899 \r
7900   if (pausing != prevPausing) {\r
7901     prevPausing = pausing;\r
7902     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7903                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7904     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7905   }\r
7906 \r
7907   switch (gameMode) {\r
7908   case BeginningOfGame:\r
7909     if (appData.icsActive)\r
7910       nowChecked = IDM_IcsClient;\r
7911     else if (appData.noChessProgram)\r
7912       nowChecked = IDM_EditGame;\r
7913     else\r
7914       nowChecked = IDM_MachineBlack;\r
7915     break;\r
7916   case MachinePlaysBlack:\r
7917     nowChecked = IDM_MachineBlack;\r
7918     break;\r
7919   case MachinePlaysWhite:\r
7920     nowChecked = IDM_MachineWhite;\r
7921     break;\r
7922   case TwoMachinesPlay:\r
7923     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7924     break;\r
7925   case AnalyzeMode:\r
7926     nowChecked = IDM_AnalysisMode;\r
7927     break;\r
7928   case AnalyzeFile:\r
7929     nowChecked = IDM_AnalyzeFile;\r
7930     break;\r
7931   case EditGame:\r
7932     nowChecked = IDM_EditGame;\r
7933     break;\r
7934   case PlayFromGameFile:\r
7935     nowChecked = IDM_LoadGame;\r
7936     break;\r
7937   case EditPosition:\r
7938     nowChecked = IDM_EditPosition;\r
7939     break;\r
7940   case Training:\r
7941     nowChecked = IDM_Training;\r
7942     break;\r
7943   case IcsPlayingWhite:\r
7944   case IcsPlayingBlack:\r
7945   case IcsObserving:\r
7946   case IcsIdle:\r
7947     nowChecked = IDM_IcsClient;\r
7948     break;\r
7949   default:\r
7950   case EndOfGame:\r
7951     nowChecked = 0;\r
7952     break;\r
7953   }\r
7954   if (prevChecked != 0)\r
7955     (void) CheckMenuItem(GetMenu(hwndMain),\r
7956                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7957   if (nowChecked != 0)\r
7958     (void) CheckMenuItem(GetMenu(hwndMain),\r
7959                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7960 \r
7961   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7962     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7963                           MF_BYCOMMAND|MF_ENABLED);\r
7964   } else {\r
7965     (void) EnableMenuItem(GetMenu(hwndMain), \r
7966                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7967   }\r
7968 \r
7969   prevChecked = nowChecked;\r
7970 \r
7971   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7972   if (appData.icsActive) {\r
7973        if (appData.icsEngineAnalyze) {\r
7974                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7975                        MF_BYCOMMAND|MF_CHECKED);\r
7976        } else {\r
7977                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7978                        MF_BYCOMMAND|MF_UNCHECKED);\r
7979        }\r
7980   }\r
7981 }\r
7982 \r
7983 VOID\r
7984 SetICSMode()\r
7985 {\r
7986   HMENU hmenu = GetMenu(hwndMain);\r
7987   SetMenuEnables(hmenu, icsEnables);\r
7988   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7989     MF_BYPOSITION|MF_ENABLED);\r
7990 #if ZIPPY\r
7991   if (appData.zippyPlay) {\r
7992     SetMenuEnables(hmenu, zippyEnables);\r
7993     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7994          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7995           MF_BYCOMMAND|MF_ENABLED);\r
7996   }\r
7997 #endif\r
7998 }\r
7999 \r
8000 VOID\r
8001 SetGNUMode()\r
8002 {\r
8003   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8004 }\r
8005 \r
8006 VOID\r
8007 SetNCPMode()\r
8008 {\r
8009   HMENU hmenu = GetMenu(hwndMain);\r
8010   SetMenuEnables(hmenu, ncpEnables);\r
8011   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8012     MF_BYPOSITION|MF_GRAYED);\r
8013     DrawMenuBar(hwndMain);\r
8014 }\r
8015 \r
8016 VOID\r
8017 SetCmailMode()\r
8018 {\r
8019   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8020 }\r
8021 \r
8022 VOID \r
8023 SetTrainingModeOn()\r
8024 {\r
8025   int i;\r
8026   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8027   for (i = 0; i < N_BUTTONS; i++) {\r
8028     if (buttonDesc[i].hwnd != NULL)\r
8029       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8030   }\r
8031   CommentPopDown();\r
8032 }\r
8033 \r
8034 VOID SetTrainingModeOff()\r
8035 {\r
8036   int i;\r
8037   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8038   for (i = 0; i < N_BUTTONS; i++) {\r
8039     if (buttonDesc[i].hwnd != NULL)\r
8040       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8041   }\r
8042 }\r
8043 \r
8044 \r
8045 VOID\r
8046 SetUserThinkingEnables()\r
8047 {\r
8048   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8049 }\r
8050 \r
8051 VOID\r
8052 SetMachineThinkingEnables()\r
8053 {\r
8054   HMENU hMenu = GetMenu(hwndMain);\r
8055   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8056 \r
8057   SetMenuEnables(hMenu, machineThinkingEnables);\r
8058 \r
8059   if (gameMode == MachinePlaysBlack) {\r
8060     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8061   } else if (gameMode == MachinePlaysWhite) {\r
8062     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8063   } else if (gameMode == TwoMachinesPlay) {\r
8064     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8065   }\r
8066 }\r
8067 \r
8068 \r
8069 VOID\r
8070 DisplayTitle(char *str)\r
8071 {\r
8072   char title[MSG_SIZ], *host;\r
8073   if (str[0] != NULLCHAR) {\r
8074     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8075   } else if (appData.icsActive) {\r
8076     if (appData.icsCommPort[0] != NULLCHAR)\r
8077       host = "ICS";\r
8078     else \r
8079       host = appData.icsHost;\r
8080       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8081   } else if (appData.noChessProgram) {\r
8082     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8083   } else {\r
8084     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8085     strcat(title, ": ");\r
8086     strcat(title, first.tidy);\r
8087   }\r
8088   SetWindowText(hwndMain, title);\r
8089 }\r
8090 \r
8091 \r
8092 VOID\r
8093 DisplayMessage(char *str1, char *str2)\r
8094 {\r
8095   HDC hdc;\r
8096   HFONT oldFont;\r
8097   int remain = MESSAGE_TEXT_MAX - 1;\r
8098   int len;\r
8099 \r
8100   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8101   messageText[0] = NULLCHAR;\r
8102   if (*str1) {\r
8103     len = strlen(str1);\r
8104     if (len > remain) len = remain;\r
8105     strncpy(messageText, str1, len);\r
8106     messageText[len] = NULLCHAR;\r
8107     remain -= len;\r
8108   }\r
8109   if (*str2 && remain >= 2) {\r
8110     if (*str1) {\r
8111       strcat(messageText, "  ");\r
8112       remain -= 2;\r
8113     }\r
8114     len = strlen(str2);\r
8115     if (len > remain) len = remain;\r
8116     strncat(messageText, str2, len);\r
8117   }\r
8118   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8119 \r
8120   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8121 \r
8122   SAYMACHINEMOVE();\r
8123 \r
8124   hdc = GetDC(hwndMain);\r
8125   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8126   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8127              &messageRect, messageText, strlen(messageText), NULL);\r
8128   (void) SelectObject(hdc, oldFont);\r
8129   (void) ReleaseDC(hwndMain, hdc);\r
8130 }\r
8131 \r
8132 VOID\r
8133 DisplayError(char *str, int error)\r
8134 {\r
8135   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8136   int len;\r
8137 \r
8138   if (error == 0) {\r
8139     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8140   } else {\r
8141     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8142                         NULL, error, LANG_NEUTRAL,\r
8143                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8144     if (len > 0) {\r
8145       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8146     } else {\r
8147       ErrorMap *em = errmap;\r
8148       while (em->err != 0 && em->err != error) em++;\r
8149       if (em->err != 0) {\r
8150         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8151       } else {\r
8152         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8153       }\r
8154     }\r
8155   }\r
8156   \r
8157   ErrorPopUp(_("Error"), buf);\r
8158 }\r
8159 \r
8160 \r
8161 VOID\r
8162 DisplayMoveError(char *str)\r
8163 {\r
8164   fromX = fromY = -1;\r
8165   ClearHighlights();\r
8166   DrawPosition(FALSE, NULL);\r
8167   if (appData.popupMoveErrors) {\r
8168     ErrorPopUp(_("Error"), str);\r
8169   } else {\r
8170     DisplayMessage(str, "");\r
8171     moveErrorMessageUp = TRUE;\r
8172   }\r
8173 }\r
8174 \r
8175 VOID\r
8176 DisplayFatalError(char *str, int error, int exitStatus)\r
8177 {\r
8178   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8179   int len;\r
8180   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8181 \r
8182   if (error != 0) {\r
8183     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8184                         NULL, error, LANG_NEUTRAL,\r
8185                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8186     if (len > 0) {\r
8187       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8188     } else {\r
8189       ErrorMap *em = errmap;\r
8190       while (em->err != 0 && em->err != error) em++;\r
8191       if (em->err != 0) {\r
8192         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8193       } else {\r
8194         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8195       }\r
8196     }\r
8197     str = buf;\r
8198   }\r
8199   if (appData.debugMode) {\r
8200     fprintf(debugFP, "%s: %s\n", label, str);\r
8201   }\r
8202   if (appData.popupExitMessage) {\r
8203     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8204                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8205   }\r
8206   ExitEvent(exitStatus);\r
8207 }\r
8208 \r
8209 \r
8210 VOID\r
8211 DisplayInformation(char *str)\r
8212 {\r
8213   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8214 }\r
8215 \r
8216 \r
8217 VOID\r
8218 DisplayNote(char *str)\r
8219 {\r
8220   ErrorPopUp(_("Note"), str);\r
8221 }\r
8222 \r
8223 \r
8224 typedef struct {\r
8225   char *title, *question, *replyPrefix;\r
8226   ProcRef pr;\r
8227 } QuestionParams;\r
8228 \r
8229 LRESULT CALLBACK\r
8230 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8231 {\r
8232   static QuestionParams *qp;\r
8233   char reply[MSG_SIZ];\r
8234   int len, err;\r
8235 \r
8236   switch (message) {\r
8237   case WM_INITDIALOG:\r
8238     qp = (QuestionParams *) lParam;\r
8239     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8240     Translate(hDlg, DLG_Question);\r
8241     SetWindowText(hDlg, qp->title);\r
8242     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8243     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8244     return FALSE;\r
8245 \r
8246   case WM_COMMAND:\r
8247     switch (LOWORD(wParam)) {\r
8248     case IDOK:\r
8249       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8250       if (*reply) strcat(reply, " ");\r
8251       len = strlen(reply);\r
8252       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8253       strcat(reply, "\n");\r
8254       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8255       EndDialog(hDlg, TRUE);\r
8256       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8257       return TRUE;\r
8258     case IDCANCEL:\r
8259       EndDialog(hDlg, FALSE);\r
8260       return TRUE;\r
8261     default:\r
8262       break;\r
8263     }\r
8264     break;\r
8265   }\r
8266   return FALSE;\r
8267 }\r
8268 \r
8269 VOID\r
8270 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8271 {\r
8272     QuestionParams qp;\r
8273     FARPROC lpProc;\r
8274     \r
8275     qp.title = title;\r
8276     qp.question = question;\r
8277     qp.replyPrefix = replyPrefix;\r
8278     qp.pr = pr;\r
8279     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8280     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8281       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8282     FreeProcInstance(lpProc);\r
8283 }\r
8284 \r
8285 /* [AS] Pick FRC position */\r
8286 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8287 {\r
8288     static int * lpIndexFRC;\r
8289     BOOL index_is_ok;\r
8290     char buf[16];\r
8291 \r
8292     switch( message )\r
8293     {\r
8294     case WM_INITDIALOG:\r
8295         lpIndexFRC = (int *) lParam;\r
8296 \r
8297         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8298         Translate(hDlg, DLG_NewGameFRC);\r
8299 \r
8300         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8301         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8302         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8303         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8304 \r
8305         break;\r
8306 \r
8307     case WM_COMMAND:\r
8308         switch( LOWORD(wParam) ) {\r
8309         case IDOK:\r
8310             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8311             EndDialog( hDlg, 0 );\r
8312             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8313             return TRUE;\r
8314         case IDCANCEL:\r
8315             EndDialog( hDlg, 1 );   \r
8316             return TRUE;\r
8317         case IDC_NFG_Edit:\r
8318             if( HIWORD(wParam) == EN_CHANGE ) {\r
8319                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8320 \r
8321                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8322             }\r
8323             return TRUE;\r
8324         case IDC_NFG_Random:\r
8325           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8326             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8327             return TRUE;\r
8328         }\r
8329 \r
8330         break;\r
8331     }\r
8332 \r
8333     return FALSE;\r
8334 }\r
8335 \r
8336 int NewGameFRC()\r
8337 {\r
8338     int result;\r
8339     int index = appData.defaultFrcPosition;\r
8340     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8341 \r
8342     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8343 \r
8344     if( result == 0 ) {\r
8345         appData.defaultFrcPosition = index;\r
8346     }\r
8347 \r
8348     return result;\r
8349 }\r
8350 \r
8351 /* [AS] Game list options. Refactored by HGM */\r
8352 \r
8353 HWND gameListOptionsDialog;\r
8354 \r
8355 // low-level front-end: clear text edit / list widget\r
8356 void\r
8357 GLT_ClearList()\r
8358 {\r
8359     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8360 }\r
8361 \r
8362 // low-level front-end: clear text edit / list widget\r
8363 void\r
8364 GLT_DeSelectList()\r
8365 {\r
8366     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8367 }\r
8368 \r
8369 // low-level front-end: append line to text edit / list widget\r
8370 void\r
8371 GLT_AddToList( char *name )\r
8372 {\r
8373     if( name != 0 ) {\r
8374             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8375     }\r
8376 }\r
8377 \r
8378 // low-level front-end: get line from text edit / list widget\r
8379 Boolean\r
8380 GLT_GetFromList( int index, char *name )\r
8381 {\r
8382     if( name != 0 ) {\r
8383             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8384                 return TRUE;\r
8385     }\r
8386     return FALSE;\r
8387 }\r
8388 \r
8389 void GLT_MoveSelection( HWND hDlg, int delta )\r
8390 {\r
8391     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8392     int idx2 = idx1 + delta;\r
8393     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8394 \r
8395     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8396         char buf[128];\r
8397 \r
8398         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8399         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8400         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8401         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8402     }\r
8403 }\r
8404 \r
8405 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8406 {\r
8407     switch( message )\r
8408     {\r
8409     case WM_INITDIALOG:\r
8410         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8411         \r
8412         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8413         Translate(hDlg, DLG_GameListOptions);\r
8414 \r
8415         /* Initialize list */\r
8416         GLT_TagsToList( lpUserGLT );\r
8417 \r
8418         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8419 \r
8420         break;\r
8421 \r
8422     case WM_COMMAND:\r
8423         switch( LOWORD(wParam) ) {\r
8424         case IDOK:\r
8425             GLT_ParseList();\r
8426             EndDialog( hDlg, 0 );\r
8427             return TRUE;\r
8428         case IDCANCEL:\r
8429             EndDialog( hDlg, 1 );\r
8430             return TRUE;\r
8431 \r
8432         case IDC_GLT_Default:\r
8433             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8434             return TRUE;\r
8435 \r
8436         case IDC_GLT_Restore:\r
8437             GLT_TagsToList( appData.gameListTags );\r
8438             return TRUE;\r
8439 \r
8440         case IDC_GLT_Up:\r
8441             GLT_MoveSelection( hDlg, -1 );\r
8442             return TRUE;\r
8443 \r
8444         case IDC_GLT_Down:\r
8445             GLT_MoveSelection( hDlg, +1 );\r
8446             return TRUE;\r
8447         }\r
8448 \r
8449         break;\r
8450     }\r
8451 \r
8452     return FALSE;\r
8453 }\r
8454 \r
8455 int GameListOptions()\r
8456 {\r
8457     int result;\r
8458     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8459 \r
8460       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8461 \r
8462     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8463 \r
8464     if( result == 0 ) {\r
8465         /* [AS] Memory leak here! */\r
8466         appData.gameListTags = strdup( lpUserGLT ); \r
8467     }\r
8468 \r
8469     return result;\r
8470 }\r
8471 \r
8472 VOID\r
8473 DisplayIcsInteractionTitle(char *str)\r
8474 {\r
8475   char consoleTitle[MSG_SIZ];\r
8476 \r
8477     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8478   SetWindowText(hwndConsole, consoleTitle);\r
8479 }\r
8480 \r
8481 void\r
8482 DrawPosition(int fullRedraw, Board board)\r
8483 {\r
8484   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8485 }\r
8486 \r
8487 void NotifyFrontendLogin()\r
8488 {\r
8489         if (hwndConsole)\r
8490                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8491 }\r
8492 \r
8493 VOID\r
8494 ResetFrontEnd()\r
8495 {\r
8496   fromX = fromY = -1;\r
8497   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8498     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8499     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8500     dragInfo.lastpos = dragInfo.pos;\r
8501     dragInfo.start.x = dragInfo.start.y = -1;\r
8502     dragInfo.from = dragInfo.start;\r
8503     ReleaseCapture();\r
8504     DrawPosition(TRUE, NULL);\r
8505   }\r
8506   TagsPopDown();\r
8507 }\r
8508 \r
8509 \r
8510 VOID\r
8511 CommentPopUp(char *title, char *str)\r
8512 {\r
8513   HWND hwnd = GetActiveWindow();\r
8514   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8515   SAY(str);\r
8516   SetActiveWindow(hwnd);\r
8517 }\r
8518 \r
8519 VOID\r
8520 CommentPopDown(void)\r
8521 {\r
8522   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8523   if (commentDialog) {\r
8524     ShowWindow(commentDialog, SW_HIDE);\r
8525   }\r
8526   commentUp = FALSE;\r
8527 }\r
8528 \r
8529 VOID\r
8530 EditCommentPopUp(int index, char *title, char *str)\r
8531 {\r
8532   EitherCommentPopUp(index, title, str, TRUE);\r
8533 }\r
8534 \r
8535 \r
8536 VOID\r
8537 RingBell()\r
8538 {\r
8539   MyPlaySound(&sounds[(int)SoundMove]);\r
8540 }\r
8541 \r
8542 VOID PlayIcsWinSound()\r
8543 {\r
8544   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8545 }\r
8546 \r
8547 VOID PlayIcsLossSound()\r
8548 {\r
8549   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8550 }\r
8551 \r
8552 VOID PlayIcsDrawSound()\r
8553 {\r
8554   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8555 }\r
8556 \r
8557 VOID PlayIcsUnfinishedSound()\r
8558 {\r
8559   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8560 }\r
8561 \r
8562 VOID\r
8563 PlayAlarmSound()\r
8564 {\r
8565   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8566 }\r
8567 \r
8568 \r
8569 VOID\r
8570 EchoOn()\r
8571 {\r
8572   HWND hInput;\r
8573   consoleEcho = TRUE;\r
8574   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8575   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8576   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8577 }\r
8578 \r
8579 \r
8580 VOID\r
8581 EchoOff()\r
8582 {\r
8583   CHARFORMAT cf;\r
8584   HWND hInput;\r
8585   consoleEcho = FALSE;\r
8586   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8587   /* This works OK: set text and background both to the same color */\r
8588   cf = consoleCF;\r
8589   cf.crTextColor = COLOR_ECHOOFF;\r
8590   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8591   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8592 }\r
8593 \r
8594 /* No Raw()...? */\r
8595 \r
8596 void Colorize(ColorClass cc, int continuation)\r
8597 {\r
8598   currentColorClass = cc;\r
8599   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8600   consoleCF.crTextColor = textAttribs[cc].color;\r
8601   consoleCF.dwEffects = textAttribs[cc].effects;\r
8602   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8603 }\r
8604 \r
8605 char *\r
8606 UserName()\r
8607 {\r
8608   static char buf[MSG_SIZ];\r
8609   DWORD bufsiz = MSG_SIZ;\r
8610 \r
8611   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8612         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8613   }\r
8614   if (!GetUserName(buf, &bufsiz)) {\r
8615     /*DisplayError("Error getting user name", GetLastError());*/\r
8616     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8617   }\r
8618   return buf;\r
8619 }\r
8620 \r
8621 char *\r
8622 HostName()\r
8623 {\r
8624   static char buf[MSG_SIZ];\r
8625   DWORD bufsiz = MSG_SIZ;\r
8626 \r
8627   if (!GetComputerName(buf, &bufsiz)) {\r
8628     /*DisplayError("Error getting host name", GetLastError());*/\r
8629     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8630   }\r
8631   return buf;\r
8632 }\r
8633 \r
8634 \r
8635 int\r
8636 ClockTimerRunning()\r
8637 {\r
8638   return clockTimerEvent != 0;\r
8639 }\r
8640 \r
8641 int\r
8642 StopClockTimer()\r
8643 {\r
8644   if (clockTimerEvent == 0) return FALSE;\r
8645   KillTimer(hwndMain, clockTimerEvent);\r
8646   clockTimerEvent = 0;\r
8647   return TRUE;\r
8648 }\r
8649 \r
8650 void\r
8651 StartClockTimer(long millisec)\r
8652 {\r
8653   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8654                              (UINT) millisec, NULL);\r
8655 }\r
8656 \r
8657 void\r
8658 DisplayWhiteClock(long timeRemaining, int highlight)\r
8659 {\r
8660   HDC hdc;\r
8661   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8662 \r
8663   if(appData.noGUI) return;\r
8664   hdc = GetDC(hwndMain);\r
8665   if (!IsIconic(hwndMain)) {\r
8666     DisplayAClock(hdc, timeRemaining, highlight, \r
8667                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8668   }\r
8669   if (highlight && iconCurrent == iconBlack) {\r
8670     iconCurrent = iconWhite;\r
8671     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8672     if (IsIconic(hwndMain)) {\r
8673       DrawIcon(hdc, 2, 2, iconCurrent);\r
8674     }\r
8675   }\r
8676   (void) ReleaseDC(hwndMain, hdc);\r
8677   if (hwndConsole)\r
8678     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8679 }\r
8680 \r
8681 void\r
8682 DisplayBlackClock(long timeRemaining, int highlight)\r
8683 {\r
8684   HDC hdc;\r
8685   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8686 \r
8687   if(appData.noGUI) return;\r
8688   hdc = GetDC(hwndMain);\r
8689   if (!IsIconic(hwndMain)) {\r
8690     DisplayAClock(hdc, timeRemaining, highlight, \r
8691                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8692   }\r
8693   if (highlight && iconCurrent == iconWhite) {\r
8694     iconCurrent = iconBlack;\r
8695     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8696     if (IsIconic(hwndMain)) {\r
8697       DrawIcon(hdc, 2, 2, iconCurrent);\r
8698     }\r
8699   }\r
8700   (void) ReleaseDC(hwndMain, hdc);\r
8701   if (hwndConsole)\r
8702     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8703 }\r
8704 \r
8705 \r
8706 int\r
8707 LoadGameTimerRunning()\r
8708 {\r
8709   return loadGameTimerEvent != 0;\r
8710 }\r
8711 \r
8712 int\r
8713 StopLoadGameTimer()\r
8714 {\r
8715   if (loadGameTimerEvent == 0) return FALSE;\r
8716   KillTimer(hwndMain, loadGameTimerEvent);\r
8717   loadGameTimerEvent = 0;\r
8718   return TRUE;\r
8719 }\r
8720 \r
8721 void\r
8722 StartLoadGameTimer(long millisec)\r
8723 {\r
8724   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8725                                 (UINT) millisec, NULL);\r
8726 }\r
8727 \r
8728 void\r
8729 AutoSaveGame()\r
8730 {\r
8731   char *defName;\r
8732   FILE *f;\r
8733   char fileTitle[MSG_SIZ];\r
8734 \r
8735   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8736   f = OpenFileDialog(hwndMain, "a", defName,\r
8737                      appData.oldSaveStyle ? "gam" : "pgn",\r
8738                      GAME_FILT, \r
8739                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8740   if (f != NULL) {\r
8741     SaveGame(f, 0, "");\r
8742     fclose(f);\r
8743   }\r
8744 }\r
8745 \r
8746 \r
8747 void\r
8748 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8749 {\r
8750   if (delayedTimerEvent != 0) {\r
8751     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8752       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8753     }\r
8754     KillTimer(hwndMain, delayedTimerEvent);\r
8755     delayedTimerEvent = 0;\r
8756     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8757     delayedTimerCallback();\r
8758   }\r
8759   delayedTimerCallback = cb;\r
8760   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8761                                 (UINT) millisec, NULL);\r
8762 }\r
8763 \r
8764 DelayedEventCallback\r
8765 GetDelayedEvent()\r
8766 {\r
8767   if (delayedTimerEvent) {\r
8768     return delayedTimerCallback;\r
8769   } else {\r
8770     return NULL;\r
8771   }\r
8772 }\r
8773 \r
8774 void\r
8775 CancelDelayedEvent()\r
8776 {\r
8777   if (delayedTimerEvent) {\r
8778     KillTimer(hwndMain, delayedTimerEvent);\r
8779     delayedTimerEvent = 0;\r
8780   }\r
8781 }\r
8782 \r
8783 DWORD GetWin32Priority(int nice)\r
8784 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8785 /*\r
8786 REALTIME_PRIORITY_CLASS     0x00000100\r
8787 HIGH_PRIORITY_CLASS         0x00000080\r
8788 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8789 NORMAL_PRIORITY_CLASS       0x00000020\r
8790 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8791 IDLE_PRIORITY_CLASS         0x00000040\r
8792 */\r
8793         if (nice < -15) return 0x00000080;\r
8794         if (nice < 0)   return 0x00008000;\r
8795         if (nice == 0)  return 0x00000020;\r
8796         if (nice < 15)  return 0x00004000;\r
8797         return 0x00000040;\r
8798 }\r
8799 \r
8800 /* Start a child process running the given program.\r
8801    The process's standard output can be read from "from", and its\r
8802    standard input can be written to "to".\r
8803    Exit with fatal error if anything goes wrong.\r
8804    Returns an opaque pointer that can be used to destroy the process\r
8805    later.\r
8806 */\r
8807 int\r
8808 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8809 {\r
8810 #define BUFSIZE 4096\r
8811 \r
8812   HANDLE hChildStdinRd, hChildStdinWr,\r
8813     hChildStdoutRd, hChildStdoutWr;\r
8814   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8815   SECURITY_ATTRIBUTES saAttr;\r
8816   BOOL fSuccess;\r
8817   PROCESS_INFORMATION piProcInfo;\r
8818   STARTUPINFO siStartInfo;\r
8819   ChildProc *cp;\r
8820   char buf[MSG_SIZ];\r
8821   DWORD err;\r
8822 \r
8823   if (appData.debugMode) {\r
8824     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8825   }\r
8826 \r
8827   *pr = NoProc;\r
8828 \r
8829   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8830   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8831   saAttr.bInheritHandle = TRUE;\r
8832   saAttr.lpSecurityDescriptor = NULL;\r
8833 \r
8834   /*\r
8835    * The steps for redirecting child's STDOUT:\r
8836    *     1. Create anonymous pipe to be STDOUT for child.\r
8837    *     2. Create a noninheritable duplicate of read handle,\r
8838    *         and close the inheritable read handle.\r
8839    */\r
8840 \r
8841   /* Create a pipe for the child's STDOUT. */\r
8842   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8843     return GetLastError();\r
8844   }\r
8845 \r
8846   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8847   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8848                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8849                              FALSE,     /* not inherited */\r
8850                              DUPLICATE_SAME_ACCESS);\r
8851   if (! fSuccess) {\r
8852     return GetLastError();\r
8853   }\r
8854   CloseHandle(hChildStdoutRd);\r
8855 \r
8856   /*\r
8857    * The steps for redirecting child's STDIN:\r
8858    *     1. Create anonymous pipe to be STDIN for child.\r
8859    *     2. Create a noninheritable duplicate of write handle,\r
8860    *         and close the inheritable write handle.\r
8861    */\r
8862 \r
8863   /* Create a pipe for the child's STDIN. */\r
8864   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8865     return GetLastError();\r
8866   }\r
8867 \r
8868   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8869   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8870                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8871                              FALSE,     /* not inherited */\r
8872                              DUPLICATE_SAME_ACCESS);\r
8873   if (! fSuccess) {\r
8874     return GetLastError();\r
8875   }\r
8876   CloseHandle(hChildStdinWr);\r
8877 \r
8878   /* Arrange to (1) look in dir for the child .exe file, and\r
8879    * (2) have dir be the child's working directory.  Interpret\r
8880    * dir relative to the directory WinBoard loaded from. */\r
8881   GetCurrentDirectory(MSG_SIZ, buf);\r
8882   SetCurrentDirectory(installDir);\r
8883   SetCurrentDirectory(dir);\r
8884 \r
8885   /* Now create the child process. */\r
8886 \r
8887   siStartInfo.cb = sizeof(STARTUPINFO);\r
8888   siStartInfo.lpReserved = NULL;\r
8889   siStartInfo.lpDesktop = NULL;\r
8890   siStartInfo.lpTitle = NULL;\r
8891   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8892   siStartInfo.cbReserved2 = 0;\r
8893   siStartInfo.lpReserved2 = NULL;\r
8894   siStartInfo.hStdInput = hChildStdinRd;\r
8895   siStartInfo.hStdOutput = hChildStdoutWr;\r
8896   siStartInfo.hStdError = hChildStdoutWr;\r
8897 \r
8898   fSuccess = CreateProcess(NULL,\r
8899                            cmdLine,        /* command line */\r
8900                            NULL,           /* process security attributes */\r
8901                            NULL,           /* primary thread security attrs */\r
8902                            TRUE,           /* handles are inherited */\r
8903                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8904                            NULL,           /* use parent's environment */\r
8905                            NULL,\r
8906                            &siStartInfo, /* STARTUPINFO pointer */\r
8907                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8908 \r
8909   err = GetLastError();\r
8910   SetCurrentDirectory(buf); /* return to prev directory */\r
8911   if (! fSuccess) {\r
8912     return err;\r
8913   }\r
8914 \r
8915   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8916     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8917     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8918   }\r
8919 \r
8920   /* Close the handles we don't need in the parent */\r
8921   CloseHandle(piProcInfo.hThread);\r
8922   CloseHandle(hChildStdinRd);\r
8923   CloseHandle(hChildStdoutWr);\r
8924 \r
8925   /* Prepare return value */\r
8926   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8927   cp->kind = CPReal;\r
8928   cp->hProcess = piProcInfo.hProcess;\r
8929   cp->pid = piProcInfo.dwProcessId;\r
8930   cp->hFrom = hChildStdoutRdDup;\r
8931   cp->hTo = hChildStdinWrDup;\r
8932 \r
8933   *pr = (void *) cp;\r
8934 \r
8935   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8936      2000 where engines sometimes don't see the initial command(s)\r
8937      from WinBoard and hang.  I don't understand how that can happen,\r
8938      but the Sleep is harmless, so I've put it in.  Others have also\r
8939      reported what may be the same problem, so hopefully this will fix\r
8940      it for them too.  */\r
8941   Sleep(500);\r
8942 \r
8943   return NO_ERROR;\r
8944 }\r
8945 \r
8946 \r
8947 void\r
8948 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8949 {\r
8950   ChildProc *cp; int result;\r
8951 \r
8952   cp = (ChildProc *) pr;\r
8953   if (cp == NULL) return;\r
8954 \r
8955   switch (cp->kind) {\r
8956   case CPReal:\r
8957     /* TerminateProcess is considered harmful, so... */\r
8958     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8959     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8960     /* The following doesn't work because the chess program\r
8961        doesn't "have the same console" as WinBoard.  Maybe\r
8962        we could arrange for this even though neither WinBoard\r
8963        nor the chess program uses a console for stdio? */\r
8964     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8965 \r
8966     /* [AS] Special termination modes for misbehaving programs... */\r
8967     if( signal == 9 ) { \r
8968         result = TerminateProcess( cp->hProcess, 0 );\r
8969 \r
8970         if ( appData.debugMode) {\r
8971             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8972         }\r
8973     }\r
8974     else if( signal == 10 ) {\r
8975         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8976 \r
8977         if( dw != WAIT_OBJECT_0 ) {\r
8978             result = TerminateProcess( cp->hProcess, 0 );\r
8979 \r
8980             if ( appData.debugMode) {\r
8981                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8982             }\r
8983 \r
8984         }\r
8985     }\r
8986 \r
8987     CloseHandle(cp->hProcess);\r
8988     break;\r
8989 \r
8990   case CPComm:\r
8991     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8992     break;\r
8993 \r
8994   case CPSock:\r
8995     closesocket(cp->sock);\r
8996     WSACleanup();\r
8997     break;\r
8998 \r
8999   case CPRcmd:\r
9000     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9001     closesocket(cp->sock);\r
9002     closesocket(cp->sock2);\r
9003     WSACleanup();\r
9004     break;\r
9005   }\r
9006   free(cp);\r
9007 }\r
9008 \r
9009 void\r
9010 InterruptChildProcess(ProcRef pr)\r
9011 {\r
9012   ChildProc *cp;\r
9013 \r
9014   cp = (ChildProc *) pr;\r
9015   if (cp == NULL) return;\r
9016   switch (cp->kind) {\r
9017   case CPReal:\r
9018     /* The following doesn't work because the chess program\r
9019        doesn't "have the same console" as WinBoard.  Maybe\r
9020        we could arrange for this even though neither WinBoard\r
9021        nor the chess program uses a console for stdio */\r
9022     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9023     break;\r
9024 \r
9025   case CPComm:\r
9026   case CPSock:\r
9027     /* Can't interrupt */\r
9028     break;\r
9029 \r
9030   case CPRcmd:\r
9031     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9032     break;\r
9033   }\r
9034 }\r
9035 \r
9036 \r
9037 int\r
9038 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9039 {\r
9040   char cmdLine[MSG_SIZ];\r
9041 \r
9042   if (port[0] == NULLCHAR) {\r
9043     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9044   } else {\r
9045     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9046   }\r
9047   return StartChildProcess(cmdLine, "", pr);\r
9048 }\r
9049 \r
9050 \r
9051 /* Code to open TCP sockets */\r
9052 \r
9053 int\r
9054 OpenTCP(char *host, char *port, ProcRef *pr)\r
9055 {\r
9056   ChildProc *cp;\r
9057   int err;\r
9058   SOCKET s;\r
9059   struct sockaddr_in sa, mysa;\r
9060   struct hostent FAR *hp;\r
9061   unsigned short uport;\r
9062   WORD wVersionRequested;\r
9063   WSADATA wsaData;\r
9064 \r
9065   /* Initialize socket DLL */\r
9066   wVersionRequested = MAKEWORD(1, 1);\r
9067   err = WSAStartup(wVersionRequested, &wsaData);\r
9068   if (err != 0) return err;\r
9069 \r
9070   /* Make socket */\r
9071   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9072     err = WSAGetLastError();\r
9073     WSACleanup();\r
9074     return err;\r
9075   }\r
9076 \r
9077   /* Bind local address using (mostly) don't-care values.\r
9078    */\r
9079   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9080   mysa.sin_family = AF_INET;\r
9081   mysa.sin_addr.s_addr = INADDR_ANY;\r
9082   uport = (unsigned short) 0;\r
9083   mysa.sin_port = htons(uport);\r
9084   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9085       == SOCKET_ERROR) {\r
9086     err = WSAGetLastError();\r
9087     WSACleanup();\r
9088     return err;\r
9089   }\r
9090 \r
9091   /* Resolve remote host name */\r
9092   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9093   if (!(hp = gethostbyname(host))) {\r
9094     unsigned int b0, b1, b2, b3;\r
9095 \r
9096     err = WSAGetLastError();\r
9097 \r
9098     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9099       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9100       hp->h_addrtype = AF_INET;\r
9101       hp->h_length = 4;\r
9102       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9103       hp->h_addr_list[0] = (char *) malloc(4);\r
9104       hp->h_addr_list[0][0] = (char) b0;\r
9105       hp->h_addr_list[0][1] = (char) b1;\r
9106       hp->h_addr_list[0][2] = (char) b2;\r
9107       hp->h_addr_list[0][3] = (char) b3;\r
9108     } else {\r
9109       WSACleanup();\r
9110       return err;\r
9111     }\r
9112   }\r
9113   sa.sin_family = hp->h_addrtype;\r
9114   uport = (unsigned short) atoi(port);\r
9115   sa.sin_port = htons(uport);\r
9116   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9117 \r
9118   /* Make connection */\r
9119   if (connect(s, (struct sockaddr *) &sa,\r
9120               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9121     err = WSAGetLastError();\r
9122     WSACleanup();\r
9123     return err;\r
9124   }\r
9125 \r
9126   /* Prepare return value */\r
9127   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9128   cp->kind = CPSock;\r
9129   cp->sock = s;\r
9130   *pr = (ProcRef *) cp;\r
9131 \r
9132   return NO_ERROR;\r
9133 }\r
9134 \r
9135 int\r
9136 OpenCommPort(char *name, ProcRef *pr)\r
9137 {\r
9138   HANDLE h;\r
9139   COMMTIMEOUTS ct;\r
9140   ChildProc *cp;\r
9141   char fullname[MSG_SIZ];\r
9142 \r
9143   if (*name != '\\')\r
9144     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9145   else\r
9146     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9147 \r
9148   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9149                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9150   if (h == (HANDLE) -1) {\r
9151     return GetLastError();\r
9152   }\r
9153   hCommPort = h;\r
9154 \r
9155   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9156 \r
9157   /* Accumulate characters until a 100ms pause, then parse */\r
9158   ct.ReadIntervalTimeout = 100;\r
9159   ct.ReadTotalTimeoutMultiplier = 0;\r
9160   ct.ReadTotalTimeoutConstant = 0;\r
9161   ct.WriteTotalTimeoutMultiplier = 0;\r
9162   ct.WriteTotalTimeoutConstant = 0;\r
9163   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9164 \r
9165   /* Prepare return value */\r
9166   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9167   cp->kind = CPComm;\r
9168   cp->hFrom = h;\r
9169   cp->hTo = h;\r
9170   *pr = (ProcRef *) cp;\r
9171 \r
9172   return NO_ERROR;\r
9173 }\r
9174 \r
9175 int\r
9176 OpenLoopback(ProcRef *pr)\r
9177 {\r
9178   DisplayFatalError(_("Not implemented"), 0, 1);\r
9179   return NO_ERROR;\r
9180 }\r
9181 \r
9182 \r
9183 int\r
9184 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9185 {\r
9186   ChildProc *cp;\r
9187   int err;\r
9188   SOCKET s, s2, s3;\r
9189   struct sockaddr_in sa, mysa;\r
9190   struct hostent FAR *hp;\r
9191   unsigned short uport;\r
9192   WORD wVersionRequested;\r
9193   WSADATA wsaData;\r
9194   int fromPort;\r
9195   char stderrPortStr[MSG_SIZ];\r
9196 \r
9197   /* Initialize socket DLL */\r
9198   wVersionRequested = MAKEWORD(1, 1);\r
9199   err = WSAStartup(wVersionRequested, &wsaData);\r
9200   if (err != 0) return err;\r
9201 \r
9202   /* Resolve remote host name */\r
9203   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9204   if (!(hp = gethostbyname(host))) {\r
9205     unsigned int b0, b1, b2, b3;\r
9206 \r
9207     err = WSAGetLastError();\r
9208 \r
9209     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9210       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9211       hp->h_addrtype = AF_INET;\r
9212       hp->h_length = 4;\r
9213       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9214       hp->h_addr_list[0] = (char *) malloc(4);\r
9215       hp->h_addr_list[0][0] = (char) b0;\r
9216       hp->h_addr_list[0][1] = (char) b1;\r
9217       hp->h_addr_list[0][2] = (char) b2;\r
9218       hp->h_addr_list[0][3] = (char) b3;\r
9219     } else {\r
9220       WSACleanup();\r
9221       return err;\r
9222     }\r
9223   }\r
9224   sa.sin_family = hp->h_addrtype;\r
9225   uport = (unsigned short) 514;\r
9226   sa.sin_port = htons(uport);\r
9227   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9228 \r
9229   /* Bind local socket to unused "privileged" port address\r
9230    */\r
9231   s = INVALID_SOCKET;\r
9232   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9233   mysa.sin_family = AF_INET;\r
9234   mysa.sin_addr.s_addr = INADDR_ANY;\r
9235   for (fromPort = 1023;; fromPort--) {\r
9236     if (fromPort < 0) {\r
9237       WSACleanup();\r
9238       return WSAEADDRINUSE;\r
9239     }\r
9240     if (s == INVALID_SOCKET) {\r
9241       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9242         err = WSAGetLastError();\r
9243         WSACleanup();\r
9244         return err;\r
9245       }\r
9246     }\r
9247     uport = (unsigned short) fromPort;\r
9248     mysa.sin_port = htons(uport);\r
9249     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9250         == SOCKET_ERROR) {\r
9251       err = WSAGetLastError();\r
9252       if (err == WSAEADDRINUSE) continue;\r
9253       WSACleanup();\r
9254       return err;\r
9255     }\r
9256     if (connect(s, (struct sockaddr *) &sa,\r
9257       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9258       err = WSAGetLastError();\r
9259       if (err == WSAEADDRINUSE) {\r
9260         closesocket(s);\r
9261         s = -1;\r
9262         continue;\r
9263       }\r
9264       WSACleanup();\r
9265       return err;\r
9266     }\r
9267     break;\r
9268   }\r
9269 \r
9270   /* Bind stderr local socket to unused "privileged" port address\r
9271    */\r
9272   s2 = INVALID_SOCKET;\r
9273   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9274   mysa.sin_family = AF_INET;\r
9275   mysa.sin_addr.s_addr = INADDR_ANY;\r
9276   for (fromPort = 1023;; fromPort--) {\r
9277     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9278     if (fromPort < 0) {\r
9279       (void) closesocket(s);\r
9280       WSACleanup();\r
9281       return WSAEADDRINUSE;\r
9282     }\r
9283     if (s2 == INVALID_SOCKET) {\r
9284       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9285         err = WSAGetLastError();\r
9286         closesocket(s);\r
9287         WSACleanup();\r
9288         return err;\r
9289       }\r
9290     }\r
9291     uport = (unsigned short) fromPort;\r
9292     mysa.sin_port = htons(uport);\r
9293     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9294         == SOCKET_ERROR) {\r
9295       err = WSAGetLastError();\r
9296       if (err == WSAEADDRINUSE) continue;\r
9297       (void) closesocket(s);\r
9298       WSACleanup();\r
9299       return err;\r
9300     }\r
9301     if (listen(s2, 1) == SOCKET_ERROR) {\r
9302       err = WSAGetLastError();\r
9303       if (err == WSAEADDRINUSE) {\r
9304         closesocket(s2);\r
9305         s2 = INVALID_SOCKET;\r
9306         continue;\r
9307       }\r
9308       (void) closesocket(s);\r
9309       (void) closesocket(s2);\r
9310       WSACleanup();\r
9311       return err;\r
9312     }\r
9313     break;\r
9314   }\r
9315   prevStderrPort = fromPort; // remember port used\r
9316   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9317 \r
9318   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9319     err = WSAGetLastError();\r
9320     (void) closesocket(s);\r
9321     (void) closesocket(s2);\r
9322     WSACleanup();\r
9323     return err;\r
9324   }\r
9325 \r
9326   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9327     err = WSAGetLastError();\r
9328     (void) closesocket(s);\r
9329     (void) closesocket(s2);\r
9330     WSACleanup();\r
9331     return err;\r
9332   }\r
9333   if (*user == NULLCHAR) user = UserName();\r
9334   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9335     err = WSAGetLastError();\r
9336     (void) closesocket(s);\r
9337     (void) closesocket(s2);\r
9338     WSACleanup();\r
9339     return err;\r
9340   }\r
9341   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9342     err = WSAGetLastError();\r
9343     (void) closesocket(s);\r
9344     (void) closesocket(s2);\r
9345     WSACleanup();\r
9346     return err;\r
9347   }\r
9348 \r
9349   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9350     err = WSAGetLastError();\r
9351     (void) closesocket(s);\r
9352     (void) closesocket(s2);\r
9353     WSACleanup();\r
9354     return err;\r
9355   }\r
9356   (void) closesocket(s2);  /* Stop listening */\r
9357 \r
9358   /* Prepare return value */\r
9359   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9360   cp->kind = CPRcmd;\r
9361   cp->sock = s;\r
9362   cp->sock2 = s3;\r
9363   *pr = (ProcRef *) cp;\r
9364 \r
9365   return NO_ERROR;\r
9366 }\r
9367 \r
9368 \r
9369 InputSourceRef\r
9370 AddInputSource(ProcRef pr, int lineByLine,\r
9371                InputCallback func, VOIDSTAR closure)\r
9372 {\r
9373   InputSource *is, *is2 = NULL;\r
9374   ChildProc *cp = (ChildProc *) pr;\r
9375 \r
9376   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9377   is->lineByLine = lineByLine;\r
9378   is->func = func;\r
9379   is->closure = closure;\r
9380   is->second = NULL;\r
9381   is->next = is->buf;\r
9382   if (pr == NoProc) {\r
9383     is->kind = CPReal;\r
9384     consoleInputSource = is;\r
9385   } else {\r
9386     is->kind = cp->kind;\r
9387     /* \r
9388         [AS] Try to avoid a race condition if the thread is given control too early:\r
9389         we create all threads suspended so that the is->hThread variable can be\r
9390         safely assigned, then let the threads start with ResumeThread.\r
9391     */\r
9392     switch (cp->kind) {\r
9393     case CPReal:\r
9394       is->hFile = cp->hFrom;\r
9395       cp->hFrom = NULL; /* now owned by InputThread */\r
9396       is->hThread =\r
9397         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9398                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9399       break;\r
9400 \r
9401     case CPComm:\r
9402       is->hFile = cp->hFrom;\r
9403       cp->hFrom = NULL; /* now owned by InputThread */\r
9404       is->hThread =\r
9405         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9406                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9407       break;\r
9408 \r
9409     case CPSock:\r
9410       is->sock = cp->sock;\r
9411       is->hThread =\r
9412         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9413                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9414       break;\r
9415 \r
9416     case CPRcmd:\r
9417       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9418       *is2 = *is;\r
9419       is->sock = cp->sock;\r
9420       is->second = is2;\r
9421       is2->sock = cp->sock2;\r
9422       is2->second = is2;\r
9423       is->hThread =\r
9424         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9425                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9426       is2->hThread =\r
9427         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9428                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9429       break;\r
9430     }\r
9431 \r
9432     if( is->hThread != NULL ) {\r
9433         ResumeThread( is->hThread );\r
9434     }\r
9435 \r
9436     if( is2 != NULL && is2->hThread != NULL ) {\r
9437         ResumeThread( is2->hThread );\r
9438     }\r
9439   }\r
9440 \r
9441   return (InputSourceRef) is;\r
9442 }\r
9443 \r
9444 void\r
9445 RemoveInputSource(InputSourceRef isr)\r
9446 {\r
9447   InputSource *is;\r
9448 \r
9449   is = (InputSource *) isr;\r
9450   is->hThread = NULL;  /* tell thread to stop */\r
9451   CloseHandle(is->hThread);\r
9452   if (is->second != NULL) {\r
9453     is->second->hThread = NULL;\r
9454     CloseHandle(is->second->hThread);\r
9455   }\r
9456 }\r
9457 \r
9458 int no_wrap(char *message, int count)\r
9459 {\r
9460     ConsoleOutput(message, count, FALSE);\r
9461     return count;\r
9462 }\r
9463 \r
9464 int\r
9465 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9466 {\r
9467   DWORD dOutCount;\r
9468   int outCount = SOCKET_ERROR;\r
9469   ChildProc *cp = (ChildProc *) pr;\r
9470   static OVERLAPPED ovl;\r
9471   static int line = 0;\r
9472 \r
9473   if (pr == NoProc)\r
9474   {\r
9475     if (appData.noJoin || !appData.useInternalWrap)\r
9476       return no_wrap(message, count);\r
9477     else\r
9478     {\r
9479       int width = get_term_width();\r
9480       int len = wrap(NULL, message, count, width, &line);\r
9481       char *msg = malloc(len);\r
9482       int dbgchk;\r
9483 \r
9484       if (!msg)\r
9485         return no_wrap(message, count);\r
9486       else\r
9487       {\r
9488         dbgchk = wrap(msg, message, count, width, &line);\r
9489         if (dbgchk != len && appData.debugMode)\r
9490             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9491         ConsoleOutput(msg, len, FALSE);\r
9492         free(msg);\r
9493         return len;\r
9494       }\r
9495     }\r
9496   }\r
9497 \r
9498   if (ovl.hEvent == NULL) {\r
9499     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9500   }\r
9501   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9502 \r
9503   switch (cp->kind) {\r
9504   case CPSock:\r
9505   case CPRcmd:\r
9506     outCount = send(cp->sock, message, count, 0);\r
9507     if (outCount == SOCKET_ERROR) {\r
9508       *outError = WSAGetLastError();\r
9509     } else {\r
9510       *outError = NO_ERROR;\r
9511     }\r
9512     break;\r
9513 \r
9514   case CPReal:\r
9515     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9516                   &dOutCount, NULL)) {\r
9517       *outError = NO_ERROR;\r
9518       outCount = (int) dOutCount;\r
9519     } else {\r
9520       *outError = GetLastError();\r
9521     }\r
9522     break;\r
9523 \r
9524   case CPComm:\r
9525     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9526                             &dOutCount, &ovl);\r
9527     if (*outError == NO_ERROR) {\r
9528       outCount = (int) dOutCount;\r
9529     }\r
9530     break;\r
9531   }\r
9532   return outCount;\r
9533 }\r
9534 \r
9535 int\r
9536 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9537                        long msdelay)\r
9538 {\r
9539   /* Ignore delay, not implemented for WinBoard */\r
9540   return OutputToProcess(pr, message, count, outError);\r
9541 }\r
9542 \r
9543 \r
9544 void\r
9545 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9546                         char *buf, int count, int error)\r
9547 {\r
9548   DisplayFatalError(_("Not implemented"), 0, 1);\r
9549 }\r
9550 \r
9551 /* see wgamelist.c for Game List functions */\r
9552 /* see wedittags.c for Edit Tags functions */\r
9553 \r
9554 \r
9555 VOID\r
9556 ICSInitScript()\r
9557 {\r
9558   FILE *f;\r
9559   char buf[MSG_SIZ];\r
9560   char *dummy;\r
9561 \r
9562   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9563     f = fopen(buf, "r");\r
9564     if (f != NULL) {\r
9565       ProcessICSInitScript(f);\r
9566       fclose(f);\r
9567     }\r
9568   }\r
9569 }\r
9570 \r
9571 \r
9572 VOID\r
9573 StartAnalysisClock()\r
9574 {\r
9575   if (analysisTimerEvent) return;\r
9576   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9577                                         (UINT) 2000, NULL);\r
9578 }\r
9579 \r
9580 VOID\r
9581 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9582 {\r
9583   highlightInfo.sq[0].x = fromX;\r
9584   highlightInfo.sq[0].y = fromY;\r
9585   highlightInfo.sq[1].x = toX;\r
9586   highlightInfo.sq[1].y = toY;\r
9587 }\r
9588 \r
9589 VOID\r
9590 ClearHighlights()\r
9591 {\r
9592   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9593     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9594 }\r
9595 \r
9596 VOID\r
9597 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9598 {\r
9599   premoveHighlightInfo.sq[0].x = fromX;\r
9600   premoveHighlightInfo.sq[0].y = fromY;\r
9601   premoveHighlightInfo.sq[1].x = toX;\r
9602   premoveHighlightInfo.sq[1].y = toY;\r
9603 }\r
9604 \r
9605 VOID\r
9606 ClearPremoveHighlights()\r
9607 {\r
9608   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9609     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9610 }\r
9611 \r
9612 VOID\r
9613 ShutDownFrontEnd()\r
9614 {\r
9615   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9616   DeleteClipboardTempFiles();\r
9617 }\r
9618 \r
9619 void\r
9620 BoardToTop()\r
9621 {\r
9622     if (IsIconic(hwndMain))\r
9623       ShowWindow(hwndMain, SW_RESTORE);\r
9624 \r
9625     SetActiveWindow(hwndMain);\r
9626 }\r
9627 \r
9628 /*\r
9629  * Prototypes for animation support routines\r
9630  */\r
9631 static void ScreenSquare(int column, int row, POINT * pt);\r
9632 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9633      POINT frames[], int * nFrames);\r
9634 \r
9635 \r
9636 #define kFactor 4\r
9637 \r
9638 void\r
9639 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9640 {       // [HGM] atomic: animate blast wave\r
9641         int i;\r
9642 \r
9643         explodeInfo.fromX = fromX;\r
9644         explodeInfo.fromY = fromY;\r
9645         explodeInfo.toX = toX;\r
9646         explodeInfo.toY = toY;\r
9647         for(i=1; i<4*kFactor; i++) {\r
9648             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9649             DrawPosition(FALSE, board);\r
9650             Sleep(appData.animSpeed);\r
9651         }\r
9652         explodeInfo.radius = 0;\r
9653         DrawPosition(TRUE, board);\r
9654 }\r
9655 \r
9656 void\r
9657 AnimateMove(board, fromX, fromY, toX, toY)\r
9658      Board board;\r
9659      int fromX;\r
9660      int fromY;\r
9661      int toX;\r
9662      int toY;\r
9663 {\r
9664   ChessSquare piece;\r
9665   POINT start, finish, mid;\r
9666   POINT frames[kFactor * 2 + 1];\r
9667   int nFrames, n;\r
9668 \r
9669   if (!appData.animate) return;\r
9670   if (doingSizing) return;\r
9671   if (fromY < 0 || fromX < 0) return;\r
9672   piece = board[fromY][fromX];\r
9673   if (piece >= EmptySquare) return;\r
9674 \r
9675   ScreenSquare(fromX, fromY, &start);\r
9676   ScreenSquare(toX, toY, &finish);\r
9677 \r
9678   /* All moves except knight jumps move in straight line */\r
9679   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9680     mid.x = start.x + (finish.x - start.x) / 2;\r
9681     mid.y = start.y + (finish.y - start.y) / 2;\r
9682   } else {\r
9683     /* Knight: make straight movement then diagonal */\r
9684     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9685        mid.x = start.x + (finish.x - start.x) / 2;\r
9686        mid.y = start.y;\r
9687      } else {\r
9688        mid.x = start.x;\r
9689        mid.y = start.y + (finish.y - start.y) / 2;\r
9690      }\r
9691   }\r
9692   \r
9693   /* Don't use as many frames for very short moves */\r
9694   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9695     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9696   else\r
9697     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9698 \r
9699   animInfo.from.x = fromX;\r
9700   animInfo.from.y = fromY;\r
9701   animInfo.to.x = toX;\r
9702   animInfo.to.y = toY;\r
9703   animInfo.lastpos = start;\r
9704   animInfo.piece = piece;\r
9705   for (n = 0; n < nFrames; n++) {\r
9706     animInfo.pos = frames[n];\r
9707     DrawPosition(FALSE, NULL);\r
9708     animInfo.lastpos = animInfo.pos;\r
9709     Sleep(appData.animSpeed);\r
9710   }\r
9711   animInfo.pos = finish;\r
9712   DrawPosition(FALSE, NULL);\r
9713   animInfo.piece = EmptySquare;\r
9714   Explode(board, fromX, fromY, toX, toY);\r
9715 }\r
9716 \r
9717 /*      Convert board position to corner of screen rect and color       */\r
9718 \r
9719 static void\r
9720 ScreenSquare(column, row, pt)\r
9721      int column; int row; POINT * pt;\r
9722 {\r
9723   if (flipView) {\r
9724     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9725     pt->y = lineGap + row * (squareSize + lineGap);\r
9726   } else {\r
9727     pt->x = lineGap + column * (squareSize + lineGap);\r
9728     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9729   }\r
9730 }\r
9731 \r
9732 /*      Generate a series of frame coords from start->mid->finish.\r
9733         The movement rate doubles until the half way point is\r
9734         reached, then halves back down to the final destination,\r
9735         which gives a nice slow in/out effect. The algorithmn\r
9736         may seem to generate too many intermediates for short\r
9737         moves, but remember that the purpose is to attract the\r
9738         viewers attention to the piece about to be moved and\r
9739         then to where it ends up. Too few frames would be less\r
9740         noticeable.                                             */\r
9741 \r
9742 static void\r
9743 Tween(start, mid, finish, factor, frames, nFrames)\r
9744      POINT * start; POINT * mid;\r
9745      POINT * finish; int factor;\r
9746      POINT frames[]; int * nFrames;\r
9747 {\r
9748   int n, fraction = 1, count = 0;\r
9749 \r
9750   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9751   for (n = 0; n < factor; n++)\r
9752     fraction *= 2;\r
9753   for (n = 0; n < factor; n++) {\r
9754     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9755     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9756     count ++;\r
9757     fraction = fraction / 2;\r
9758   }\r
9759   \r
9760   /* Midpoint */\r
9761   frames[count] = *mid;\r
9762   count ++;\r
9763   \r
9764   /* Slow out, stepping 1/2, then 1/4, ... */\r
9765   fraction = 2;\r
9766   for (n = 0; n < factor; n++) {\r
9767     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9768     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9769     count ++;\r
9770     fraction = fraction * 2;\r
9771   }\r
9772   *nFrames = count;\r
9773 }\r
9774 \r
9775 void\r
9776 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9777 {\r
9778     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9779 \r
9780     EvalGraphSet( first, last, current, pvInfoList );\r
9781 }\r
9782 \r
9783 void\r
9784 SettingsPopUp(ChessProgramState *cps)\r
9785 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9786       EngineOptionsPopup(savedHwnd, cps);\r
9787 }\r