Reset win/loss counters before 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, OPT_MESS, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][10];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
349     if((f = fopen(buf, "r")) == NULL) return;\r
350     while((k = fgetc(f)) != EOF) {\r
351         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
352         languageBuf[i] = k;\r
353         if(k == '\n') {\r
354             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
355                 char *p;\r
356                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
357                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
358                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
359                         english[j] = languageBuf + n + 1; *p = 0;\r
360                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
362                     }\r
363                 }\r
364             }\r
365             n = i + 1;\r
366         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
367             switch(k) {\r
368               case 'n': k = '\n'; break;\r
369               case 'r': k = '\r'; break;\r
370               case 't': k = '\t'; break;\r
371             }\r
372             languageBuf[--i] = k;\r
373         }\r
374         i++;\r
375     }\r
376     fclose(f);\r
377     barbaric = (j != 0);\r
378     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
379 }\r
380 \r
381 char *\r
382 T_(char *s)\r
383 {   // return the translation of the given string\r
384     // efficiency can be improved a lot...\r
385     int i=0;\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
387     if(!barbaric) return s;\r
388     if(!s) return ""; // sanity\r
389     while(english[i]) {\r
390         if(!strcmp(s, english[i])) return foreign[i];\r
391         i++;\r
392     }\r
393     return s;\r
394 }\r
395 \r
396 void\r
397 Translate(HWND hDlg, int dialogID)\r
398 {   // translate all text items in the given dialog\r
399     int i=0, j, k;\r
400     char buf[MSG_SIZ], *s;\r
401     if(!barbaric) return;\r
402     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
403     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
404     GetWindowText( hDlg, buf, MSG_SIZ );\r
405     s = T_(buf);\r
406     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
407     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
408         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
409         if(strlen(buf) == 0) continue;\r
410         s = T_(buf);\r
411         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
412     }\r
413 }\r
414 \r
415 HMENU\r
416 TranslateOneMenu(int i, HMENU subMenu)\r
417 {\r
418     int j;\r
419     static MENUITEMINFO info;\r
420 \r
421     info.cbSize = sizeof(MENUITEMINFO);\r
422     info.fMask = MIIM_STATE | MIIM_TYPE;\r
423           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
424             char buf[MSG_SIZ];\r
425             info.dwTypeData = buf;\r
426             info.cch = sizeof(buf);\r
427             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
428             if(i < 10) {
429                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
430                 else menuText[i][j] = strdup(buf); // remember original on first change\r
431             }\r
432             if(buf[0] == NULLCHAR) continue;\r
433             info.dwTypeData = T_(buf);\r
434             info.cch = strlen(buf)+1;\r
435             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
436           }\r
437     return subMenu;\r
438 }\r
439 \r
440 void\r
441 TranslateMenus(int addLanguage)\r
442 {\r
443     int i;\r
444     WIN32_FIND_DATA fileData;\r
445     HANDLE hFind;\r
446 #define IDM_English 1895\r
447     if(1) {\r
448         HMENU mainMenu = GetMenu(hwndMain);\r
449         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
450           HMENU subMenu = GetSubMenu(mainMenu, i);\r
451           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
452                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
453           TranslateOneMenu(i, subMenu);\r
454         }\r
455         DrawMenuBar(hwndMain);\r
456     }\r
457 \r
458     if(!addLanguage) return;\r
459     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
462         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
463         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
464         i = 0; lastChecked = IDM_English;\r
465         do {\r
466             char *p, *q = fileData.cFileName;\r
467             int checkFlag = MF_UNCHECKED;\r
468             languageFile[i] = strdup(q);\r
469             if(barbaric && !strcmp(oldLanguage, q)) {\r
470                 checkFlag = MF_CHECKED;\r
471                 lastChecked = IDM_English + i + 1;\r
472                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
473             }\r
474             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
475             p = strstr(fileData.cFileName, ".lng");\r
476             if(p) *p = 0;\r
477             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
478         } while(FindNextFile(hFind, &fileData));\r
479         FindClose(hFind);\r
480     }\r
481 }\r
482 \r
483 #endif\r
484 \r
485 typedef struct {\r
486   char *name;\r
487   int squareSize;\r
488   int lineGap;\r
489   int smallLayout;\r
490   int tinyLayout;\r
491   int cliWidth, cliHeight;\r
492 } SizeInfo;\r
493 \r
494 SizeInfo sizeInfo[] = \r
495 {\r
496   { "tiny",     21, 0, 1, 1, 0, 0 },\r
497   { "teeny",    25, 1, 1, 1, 0, 0 },\r
498   { "dinky",    29, 1, 1, 1, 0, 0 },\r
499   { "petite",   33, 1, 1, 1, 0, 0 },\r
500   { "slim",     37, 2, 1, 0, 0, 0 },\r
501   { "small",    40, 2, 1, 0, 0, 0 },\r
502   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
503   { "middling", 49, 2, 0, 0, 0, 0 },\r
504   { "average",  54, 2, 0, 0, 0, 0 },\r
505   { "moderate", 58, 3, 0, 0, 0, 0 },\r
506   { "medium",   64, 3, 0, 0, 0, 0 },\r
507   { "bulky",    72, 3, 0, 0, 0, 0 },\r
508   { "large",    80, 3, 0, 0, 0, 0 },\r
509   { "big",      87, 3, 0, 0, 0, 0 },\r
510   { "huge",     95, 3, 0, 0, 0, 0 },\r
511   { "giant",    108, 3, 0, 0, 0, 0 },\r
512   { "colossal", 116, 4, 0, 0, 0, 0 },\r
513   { "titanic",  129, 4, 0, 0, 0, 0 },\r
514   { NULL, 0, 0, 0, 0, 0, 0 }\r
515 };\r
516 \r
517 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
518 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
519 {\r
520   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
521   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
522   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
523   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
524   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
525   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
526   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
527   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
528   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
529   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
530   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
531   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
532   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
533   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
534   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
535   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
536   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
537   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
538 };\r
539 \r
540 MyFont *font[NUM_SIZES][NUM_FONTS];\r
541 \r
542 typedef struct {\r
543   char *label;\r
544   int id;\r
545   HWND hwnd;\r
546   WNDPROC wndproc;\r
547 } MyButtonDesc;\r
548 \r
549 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
550 #define N_BUTTONS 5\r
551 \r
552 MyButtonDesc buttonDesc[N_BUTTONS] =\r
553 {\r
554   {"<<", IDM_ToStart, NULL, NULL},\r
555   {"<", IDM_Backward, NULL, NULL},\r
556   {"P", IDM_Pause, NULL, NULL},\r
557   {">", IDM_Forward, NULL, NULL},\r
558   {">>", IDM_ToEnd, NULL, NULL},\r
559 };\r
560 \r
561 int tinyLayout = 0, smallLayout = 0;\r
562 #define MENU_BAR_ITEMS 9\r
563 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
564   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
565   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
566 };\r
567 \r
568 \r
569 MySound sounds[(int)NSoundClasses];\r
570 MyTextAttribs textAttribs[(int)NColorClasses];\r
571 \r
572 MyColorizeAttribs colorizeAttribs[] = {\r
573   { (COLORREF)0, 0, N_("Shout Text") },\r
574   { (COLORREF)0, 0, N_("SShout/CShout") },\r
575   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
576   { (COLORREF)0, 0, N_("Channel Text") },\r
577   { (COLORREF)0, 0, N_("Kibitz Text") },\r
578   { (COLORREF)0, 0, N_("Tell Text") },\r
579   { (COLORREF)0, 0, N_("Challenge Text") },\r
580   { (COLORREF)0, 0, N_("Request Text") },\r
581   { (COLORREF)0, 0, N_("Seek Text") },\r
582   { (COLORREF)0, 0, N_("Normal Text") },\r
583   { (COLORREF)0, 0, N_("None") }\r
584 };\r
585 \r
586 \r
587 \r
588 static char *commentTitle;\r
589 static char *commentText;\r
590 static int commentIndex;\r
591 static Boolean editComment = FALSE;\r
592 \r
593 \r
594 char errorTitle[MSG_SIZ];\r
595 char errorMessage[2*MSG_SIZ];\r
596 HWND errorDialog = NULL;\r
597 BOOLEAN moveErrorMessageUp = FALSE;\r
598 BOOLEAN consoleEcho = TRUE;\r
599 CHARFORMAT consoleCF;\r
600 COLORREF consoleBackgroundColor;\r
601 \r
602 char *programVersion;\r
603 \r
604 #define CPReal 1\r
605 #define CPComm 2\r
606 #define CPSock 3\r
607 #define CPRcmd 4\r
608 typedef int CPKind;\r
609 \r
610 typedef struct {\r
611   CPKind kind;\r
612   HANDLE hProcess;\r
613   DWORD pid;\r
614   HANDLE hTo;\r
615   HANDLE hFrom;\r
616   SOCKET sock;\r
617   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
618 } ChildProc;\r
619 \r
620 #define INPUT_SOURCE_BUF_SIZE 4096\r
621 \r
622 typedef struct _InputSource {\r
623   CPKind kind;\r
624   HANDLE hFile;\r
625   SOCKET sock;\r
626   int lineByLine;\r
627   HANDLE hThread;\r
628   DWORD id;\r
629   char buf[INPUT_SOURCE_BUF_SIZE];\r
630   char *next;\r
631   DWORD count;\r
632   int error;\r
633   InputCallback func;\r
634   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
635   VOIDSTAR closure;\r
636 } InputSource;\r
637 \r
638 InputSource *consoleInputSource;\r
639 \r
640 DCB dcb;\r
641 \r
642 /* forward */\r
643 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
644 VOID ConsoleCreate();\r
645 LRESULT CALLBACK\r
646   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
647 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
648 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
649 VOID ParseCommSettings(char *arg, DCB *dcb);\r
650 LRESULT CALLBACK\r
651   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
652 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
653 void ParseIcsTextMenu(char *icsTextMenuString);\r
654 VOID PopUpMoveDialog(char firstchar);\r
655 VOID PopUpNameDialog(char firstchar);\r
656 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
657 \r
658 /* [AS] */\r
659 int NewGameFRC();\r
660 int GameListOptions();\r
661 \r
662 int dummy; // [HGM] for obsolete args\r
663 \r
664 HWND hwndMain = NULL;        /* root window*/\r
665 HWND hwndConsole = NULL;\r
666 HWND commentDialog = NULL;\r
667 HWND moveHistoryDialog = NULL;\r
668 HWND evalGraphDialog = NULL;\r
669 HWND engineOutputDialog = NULL;\r
670 HWND gameListDialog = NULL;\r
671 HWND editTagsDialog = NULL;\r
672 \r
673 int commentUp = FALSE;\r
674 \r
675 WindowPlacement wpMain;\r
676 WindowPlacement wpConsole;\r
677 WindowPlacement wpComment;\r
678 WindowPlacement wpMoveHistory;\r
679 WindowPlacement wpEvalGraph;\r
680 WindowPlacement wpEngineOutput;\r
681 WindowPlacement wpGameList;\r
682 WindowPlacement wpTags;\r
683 \r
684 VOID EngineOptionsPopup(); // [HGM] settings\r
685 \r
686 VOID GothicPopUp(char *title, VariantClass variant);\r
687 /*\r
688  * Setting "frozen" should disable all user input other than deleting\r
689  * the window.  We do this while engines are initializing themselves.\r
690  */\r
691 static int frozen = 0;\r
692 static int oldMenuItemState[MENU_BAR_ITEMS];\r
693 void FreezeUI()\r
694 {\r
695   HMENU hmenu;\r
696   int i;\r
697 \r
698   if (frozen) return;\r
699   frozen = 1;\r
700   hmenu = GetMenu(hwndMain);\r
701   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
702     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
703   }\r
704   DrawMenuBar(hwndMain);\r
705 }\r
706 \r
707 /* Undo a FreezeUI */\r
708 void ThawUI()\r
709 {\r
710   HMENU hmenu;\r
711   int i;\r
712 \r
713   if (!frozen) return;\r
714   frozen = 0;\r
715   hmenu = GetMenu(hwndMain);\r
716   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
717     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
718   }\r
719   DrawMenuBar(hwndMain);\r
720 }\r
721 \r
722 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
723 \r
724 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
725 #ifdef JAWS\r
726 #include "jaws.c"\r
727 #else\r
728 #define JAWS_INIT\r
729 #define JAWS_ARGS\r
730 #define JAWS_ALT_INTERCEPT\r
731 #define JAWS_KB_NAVIGATION\r
732 #define JAWS_MENU_ITEMS\r
733 #define JAWS_SILENCE\r
734 #define JAWS_REPLAY\r
735 #define JAWS_ACCEL\r
736 #define JAWS_COPYRIGHT\r
737 #define JAWS_DELETE(X) X\r
738 #define SAYMACHINEMOVE()\r
739 #define SAY(X)\r
740 #endif\r
741 \r
742 /*---------------------------------------------------------------------------*\\r
743  *\r
744  * WinMain\r
745  *\r
746 \*---------------------------------------------------------------------------*/\r
747 \r
748 int APIENTRY\r
749 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
750         LPSTR lpCmdLine, int nCmdShow)\r
751 {\r
752   MSG msg;\r
753   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
754 //  INITCOMMONCONTROLSEX ex;\r
755 \r
756   debugFP = stderr;\r
757 \r
758   LoadLibrary("RICHED32.DLL");\r
759   consoleCF.cbSize = sizeof(CHARFORMAT);\r
760 \r
761   if (!InitApplication(hInstance)) {\r
762     return (FALSE);\r
763   }\r
764   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
765     return (FALSE);\r
766   }\r
767 \r
768   JAWS_INIT\r
769   TranslateMenus(1);\r
770 \r
771 //  InitCommonControlsEx(&ex);\r
772   InitCommonControls();\r
773 \r
774   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
775   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
776   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
777 \r
778   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
779 \r
780   while (GetMessage(&msg, /* message structure */\r
781                     NULL, /* handle of window receiving the message */\r
782                     0,    /* lowest message to examine */\r
783                     0))   /* highest message to examine */\r
784     {\r
785 \r
786       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
787         // [HGM] navigate: switch between all windows with tab\r
788         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
789         int i, currentElement = 0;\r
790 \r
791         // first determine what element of the chain we come from (if any)\r
792         if(appData.icsActive) {\r
793             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
794             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
795         }\r
796         if(engineOutputDialog && EngineOutputIsUp()) {\r
797             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
798             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
799         }\r
800         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
801             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
802         }\r
803         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
804         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
805         if(msg.hwnd == e1)                 currentElement = 2; else\r
806         if(msg.hwnd == e2)                 currentElement = 3; else\r
807         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
808         if(msg.hwnd == mh)                currentElement = 4; else\r
809         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
810         if(msg.hwnd == hText)  currentElement = 5; else\r
811         if(msg.hwnd == hInput) currentElement = 6; else\r
812         for (i = 0; i < N_BUTTONS; i++) {\r
813             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
814         }\r
815 \r
816         // determine where to go to\r
817         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
818           do {\r
819             currentElement = (currentElement + direction) % 7;\r
820             switch(currentElement) {\r
821                 case 0:\r
822                   h = hwndMain; break; // passing this case always makes the loop exit\r
823                 case 1:\r
824                   h = buttonDesc[0].hwnd; break; // could be NULL\r
825                 case 2:\r
826                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
827                   h = e1; break;\r
828                 case 3:\r
829                   if(!EngineOutputIsUp()) continue;\r
830                   h = e2; break;\r
831                 case 4:\r
832                   if(!MoveHistoryIsUp()) continue;\r
833                   h = mh; break;\r
834 //              case 6: // input to eval graph does not seem to get here!\r
835 //                if(!EvalGraphIsUp()) continue;\r
836 //                h = evalGraphDialog; break;\r
837                 case 5:\r
838                   if(!appData.icsActive) continue;\r
839                   SAY("display");\r
840                   h = hText; break;\r
841                 case 6:\r
842                   if(!appData.icsActive) continue;\r
843                   SAY("input");\r
844                   h = hInput; break;\r
845             }\r
846           } while(h == 0);\r
847 \r
848           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
849           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
850           SetFocus(h);\r
851 \r
852           continue; // this message now has been processed\r
853         }\r
854       }\r
855 \r
856       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
857           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
858           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
859           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
860           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
861           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
862           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
863           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
864           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
865           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
866         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
867         for(i=0; i<MAX_CHAT; i++) \r
868             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
869                 done = 1; break;\r
870         }\r
871         if(done) continue; // [HGM] chat: end patch\r
872         TranslateMessage(&msg); /* Translates virtual key codes */\r
873         DispatchMessage(&msg);  /* Dispatches message to window */\r
874       }\r
875     }\r
876 \r
877 \r
878   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
879 }\r
880 \r
881 /*---------------------------------------------------------------------------*\\r
882  *\r
883  * Initialization functions\r
884  *\r
885 \*---------------------------------------------------------------------------*/\r
886 \r
887 void\r
888 SetUserLogo()\r
889 {   // update user logo if necessary\r
890     static char oldUserName[MSG_SIZ], *curName;\r
891 \r
892     if(appData.autoLogo) {\r
893           curName = UserName();\r
894           if(strcmp(curName, oldUserName)) {\r
895             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
896                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
897                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
898                 if(userLogo == NULL)\r
899                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
900           }\r
901     }\r
902 }\r
903 \r
904 BOOL\r
905 InitApplication(HINSTANCE hInstance)\r
906 {\r
907   WNDCLASS wc;\r
908 \r
909   /* Fill in window class structure with parameters that describe the */\r
910   /* main window. */\r
911 \r
912   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
913   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
914   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
915   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
916   wc.hInstance     = hInstance;         /* Owner of this class */\r
917   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
918   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
919   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
920   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
921   wc.lpszClassName = szAppName;                 /* Name to register as */\r
922 \r
923   /* Register the window class and return success/failure code. */\r
924   if (!RegisterClass(&wc)) return FALSE;\r
925 \r
926   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
927   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
928   wc.cbClsExtra    = 0;\r
929   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
930   wc.hInstance     = hInstance;\r
931   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
932   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
933   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
934   wc.lpszMenuName  = NULL;\r
935   wc.lpszClassName = szConsoleName;\r
936 \r
937   if (!RegisterClass(&wc)) return FALSE;\r
938   return TRUE;\r
939 }\r
940 \r
941 \r
942 /* Set by InitInstance, used by EnsureOnScreen */\r
943 int screenHeight, screenWidth;\r
944 \r
945 void\r
946 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
947 {\r
948 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
949   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
950   if (*x > screenWidth - 32) *x = 0;\r
951   if (*y > screenHeight - 32) *y = 0;\r
952   if (*x < minX) *x = minX;\r
953   if (*y < minY) *y = minY;\r
954 }\r
955 \r
956 BOOL\r
957 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
958 {\r
959   HWND hwnd; /* Main window handle. */\r
960   int ibs;\r
961   WINDOWPLACEMENT wp;\r
962   char *filepart;\r
963 \r
964   hInst = hInstance;    /* Store instance handle in our global variable */\r
965   programName = szAppName;\r
966 \r
967   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
968     *filepart = NULLCHAR;\r
969   } else {\r
970     GetCurrentDirectory(MSG_SIZ, installDir);\r
971   }\r
972   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
973   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
974   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
975   /* xboard, and older WinBoards, controlled the move sound with the\r
976      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
977      always turn the option on (so that the backend will call us),\r
978      then let the user turn the sound off by setting it to silence if\r
979      desired.  To accommodate old winboard.ini files saved by old\r
980      versions of WinBoard, we also turn off the sound if the option\r
981      was initially set to false. [HGM] taken out of InitAppData */\r
982   if (!appData.ringBellAfterMoves) {\r
983     sounds[(int)SoundMove].name = strdup("");\r
984     appData.ringBellAfterMoves = TRUE;\r
985   }\r
986   if (appData.debugMode) {\r
987     debugFP = fopen(appData.nameOfDebugFile, "w");\r
988     setbuf(debugFP, NULL);\r
989   }\r
990 \r
991   LoadLanguageFile(appData.language);\r
992 \r
993   InitBackEnd1();\r
994 \r
995 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
996 //  InitEngineUCI( installDir, &second );\r
997 \r
998   /* Create a main window for this application instance. */\r
999   hwnd = CreateWindow(szAppName, szTitle,\r
1000                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1001                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1002                       NULL, NULL, hInstance, NULL);\r
1003   hwndMain = hwnd;\r
1004 \r
1005   /* If window could not be created, return "failure" */\r
1006   if (!hwnd) {\r
1007     return (FALSE);\r
1008   }\r
1009 \r
1010   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1011   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
1012       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1013 \r
1014       if (first.programLogo == NULL && appData.debugMode) {\r
1015           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1016       }\r
1017   } else if(appData.autoLogo) {\r
1018       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1019         char buf[MSG_SIZ];\r
1020           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1021         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1022       }\r
1023   }\r
1024 \r
1025   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1026       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1027 \r
1028       if (second.programLogo == NULL && appData.debugMode) {\r
1029           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1030       }\r
1031   } else if(appData.autoLogo) {\r
1032       char buf[MSG_SIZ];\r
1033       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1034         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1035         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1036       } else\r
1037       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1038         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1039         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1040       }\r
1041   }\r
1042 \r
1043   SetUserLogo();\r
1044 \r
1045   iconWhite = LoadIcon(hInstance, "icon_white");\r
1046   iconBlack = LoadIcon(hInstance, "icon_black");\r
1047   iconCurrent = iconWhite;\r
1048   InitDrawingColors();\r
1049   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1050   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1051   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1052     /* Compute window size for each board size, and use the largest\r
1053        size that fits on this screen as the default. */\r
1054     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1055     if (boardSize == (BoardSize)-1 &&\r
1056         winH <= screenHeight\r
1057            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1058         && winW <= screenWidth) {\r
1059       boardSize = (BoardSize)ibs;\r
1060     }\r
1061   }\r
1062 \r
1063   InitDrawingSizes(boardSize, 0);\r
1064   InitMenuChecks();\r
1065   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1066 \r
1067   /* [AS] Load textures if specified */\r
1068   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1069   \r
1070   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1071       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1072       liteBackTextureMode = appData.liteBackTextureMode;\r
1073 \r
1074       if (liteBackTexture == NULL && appData.debugMode) {\r
1075           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1076       }\r
1077   }\r
1078   \r
1079   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1080       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1081       darkBackTextureMode = appData.darkBackTextureMode;\r
1082 \r
1083       if (darkBackTexture == NULL && appData.debugMode) {\r
1084           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1085       }\r
1086   }\r
1087 \r
1088   mysrandom( (unsigned) time(NULL) );\r
1089 \r
1090   /* [AS] Restore layout */\r
1091   if( wpMoveHistory.visible ) {\r
1092       MoveHistoryPopUp();\r
1093   }\r
1094 \r
1095   if( wpEvalGraph.visible ) {\r
1096       EvalGraphPopUp();\r
1097   }\r
1098 \r
1099   if( wpEngineOutput.visible ) {\r
1100       EngineOutputPopUp();\r
1101   }\r
1102 \r
1103   /* Make the window visible; update its client area; and return "success" */\r
1104   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1105   wp.length = sizeof(WINDOWPLACEMENT);\r
1106   wp.flags = 0;\r
1107   wp.showCmd = nCmdShow;\r
1108   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1109   wp.rcNormalPosition.left = wpMain.x;\r
1110   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1111   wp.rcNormalPosition.top = wpMain.y;\r
1112   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1113   SetWindowPlacement(hwndMain, &wp);\r
1114 \r
1115   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1116 \r
1117   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1118                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1119 \r
1120   if (hwndConsole) {\r
1121 #if AOT_CONSOLE\r
1122     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1123                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1124 #endif\r
1125     ShowWindow(hwndConsole, nCmdShow);\r
1126     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1127       char buf[MSG_SIZ], *p = buf, *q;\r
1128         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1129       do {\r
1130         q = strchr(p, ';');\r
1131         if(q) *q++ = 0;\r
1132         if(*p) ChatPopUp(p);\r
1133       } while(p=q);\r
1134     }\r
1135     SetActiveWindow(hwndConsole);\r
1136   }\r
1137   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1138   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1139 \r
1140   return TRUE;\r
1141 \r
1142 }\r
1143 \r
1144 VOID\r
1145 InitMenuChecks()\r
1146 {\r
1147   HMENU hmenu = GetMenu(hwndMain);\r
1148 \r
1149   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1150                         MF_BYCOMMAND|((appData.icsActive &&\r
1151                                        *appData.icsCommPort != NULLCHAR) ?\r
1152                                       MF_ENABLED : MF_GRAYED));\r
1153   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1154                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1155                                      MF_CHECKED : MF_UNCHECKED));\r
1156 }\r
1157 \r
1158 //---------------------------------------------------------------------------------------------------------\r
1159 \r
1160 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1161 #define XBOARD FALSE\r
1162 \r
1163 #define OPTCHAR "/"\r
1164 #define SEPCHAR "="\r
1165 \r
1166 #include "args.h"\r
1167 \r
1168 // front-end part of option handling\r
1169 \r
1170 VOID\r
1171 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1172 {\r
1173   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1174   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1175   DeleteDC(hdc);\r
1176   lf->lfWidth = 0;\r
1177   lf->lfEscapement = 0;\r
1178   lf->lfOrientation = 0;\r
1179   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1180   lf->lfItalic = mfp->italic;\r
1181   lf->lfUnderline = mfp->underline;\r
1182   lf->lfStrikeOut = mfp->strikeout;\r
1183   lf->lfCharSet = mfp->charset;\r
1184   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1185   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1186   lf->lfQuality = DEFAULT_QUALITY;\r
1187   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1188     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1189 }\r
1190 \r
1191 void\r
1192 CreateFontInMF(MyFont *mf)\r
1193\r
1194   LFfromMFP(&mf->lf, &mf->mfp);\r
1195   if (mf->hf) DeleteObject(mf->hf);\r
1196   mf->hf = CreateFontIndirect(&mf->lf);\r
1197 }\r
1198 \r
1199 // [HGM] This platform-dependent table provides the location for storing the color info\r
1200 void *\r
1201 colorVariable[] = {\r
1202   &whitePieceColor, \r
1203   &blackPieceColor, \r
1204   &lightSquareColor,\r
1205   &darkSquareColor, \r
1206   &highlightSquareColor,\r
1207   &premoveHighlightColor,\r
1208   NULL,\r
1209   &consoleBackgroundColor,\r
1210   &appData.fontForeColorWhite,\r
1211   &appData.fontBackColorWhite,\r
1212   &appData.fontForeColorBlack,\r
1213   &appData.fontBackColorBlack,\r
1214   &appData.evalHistColorWhite,\r
1215   &appData.evalHistColorBlack,\r
1216   &appData.highlightArrowColor,\r
1217 };\r
1218 \r
1219 /* Command line font name parser.  NULL name means do nothing.\r
1220    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1221    For backward compatibility, syntax without the colon is also\r
1222    accepted, but font names with digits in them won't work in that case.\r
1223 */\r
1224 VOID\r
1225 ParseFontName(char *name, MyFontParams *mfp)\r
1226 {\r
1227   char *p, *q;\r
1228   if (name == NULL) return;\r
1229   p = name;\r
1230   q = strchr(p, ':');\r
1231   if (q) {\r
1232     if (q - p >= sizeof(mfp->faceName))\r
1233       ExitArgError(_("Font name too long:"), name);\r
1234     memcpy(mfp->faceName, p, q - p);\r
1235     mfp->faceName[q - p] = NULLCHAR;\r
1236     p = q + 1;\r
1237   } else {\r
1238     q = mfp->faceName;\r
1239     while (*p && !isdigit(*p)) {\r
1240       *q++ = *p++;\r
1241       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1242         ExitArgError(_("Font name too long:"), name);\r
1243     }\r
1244     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1245     *q = NULLCHAR;\r
1246   }\r
1247   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1248   mfp->pointSize = (float) atof(p);\r
1249   mfp->bold = (strchr(p, 'b') != NULL);\r
1250   mfp->italic = (strchr(p, 'i') != NULL);\r
1251   mfp->underline = (strchr(p, 'u') != NULL);\r
1252   mfp->strikeout = (strchr(p, 's') != NULL);\r
1253   mfp->charset = DEFAULT_CHARSET;\r
1254   q = strchr(p, 'c');\r
1255   if (q)\r
1256     mfp->charset = (BYTE) atoi(q+1);\r
1257 }\r
1258 \r
1259 void\r
1260 ParseFont(char *name, int number)\r
1261 { // wrapper to shield back-end from 'font'\r
1262   ParseFontName(name, &font[boardSize][number]->mfp);\r
1263 }\r
1264 \r
1265 void\r
1266 SetFontDefaults()\r
1267 { // in WB  we have a 2D array of fonts; this initializes their description\r
1268   int i, j;\r
1269   /* Point font array elements to structures and\r
1270      parse default font names */\r
1271   for (i=0; i<NUM_FONTS; i++) {\r
1272     for (j=0; j<NUM_SIZES; j++) {\r
1273       font[j][i] = &fontRec[j][i];\r
1274       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1275     }\r
1276   }\r
1277 }\r
1278 \r
1279 void\r
1280 CreateFonts()\r
1281 { // here we create the actual fonts from the selected descriptions\r
1282   int i, j;\r
1283   for (i=0; i<NUM_FONTS; i++) {\r
1284     for (j=0; j<NUM_SIZES; j++) {\r
1285       CreateFontInMF(font[j][i]);\r
1286     }\r
1287   }\r
1288 }\r
1289 /* Color name parser.\r
1290    X version accepts X color names, but this one\r
1291    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1292 COLORREF\r
1293 ParseColorName(char *name)\r
1294 {\r
1295   int red, green, blue, count;\r
1296   char buf[MSG_SIZ];\r
1297 \r
1298   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1299   if (count != 3) {\r
1300     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1301       &red, &green, &blue);\r
1302   }\r
1303   if (count != 3) {\r
1304     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1305     DisplayError(buf, 0);\r
1306     return RGB(0, 0, 0);\r
1307   }\r
1308   return PALETTERGB(red, green, blue);\r
1309 }\r
1310 \r
1311 void\r
1312 ParseColor(int n, char *name)\r
1313 { // for WinBoard the color is an int, which needs to be derived from the string\r
1314   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1315 }\r
1316 \r
1317 void\r
1318 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1319 {\r
1320   char *e = argValue;\r
1321   int eff = 0;\r
1322 \r
1323   while (*e) {\r
1324     if (*e == 'b')      eff |= CFE_BOLD;\r
1325     else if (*e == 'i') eff |= CFE_ITALIC;\r
1326     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1327     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1328     else if (*e == '#' || isdigit(*e)) break;\r
1329     e++;\r
1330   }\r
1331   *effects = eff;\r
1332   *color   = ParseColorName(e);\r
1333 }\r
1334 \r
1335 void\r
1336 ParseTextAttribs(ColorClass cc, char *s)\r
1337 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1338     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1339     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1340 }\r
1341 \r
1342 void\r
1343 ParseBoardSize(void *addr, char *name)\r
1344 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1345   BoardSize bs = SizeTiny;\r
1346   while (sizeInfo[bs].name != NULL) {\r
1347     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1348         *(BoardSize *)addr = bs;\r
1349         return;\r
1350     }\r
1351     bs++;\r
1352   }\r
1353   ExitArgError(_("Unrecognized board size value"), name);\r
1354 }\r
1355 \r
1356 void\r
1357 LoadAllSounds()\r
1358 { // [HGM] import name from appData first\r
1359   ColorClass cc;\r
1360   SoundClass sc;\r
1361   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1362     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1363     textAttribs[cc].sound.data = NULL;\r
1364     MyLoadSound(&textAttribs[cc].sound);\r
1365   }\r
1366   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1367     textAttribs[cc].sound.name = strdup("");\r
1368     textAttribs[cc].sound.data = NULL;\r
1369   }\r
1370   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1371     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1372     sounds[sc].data = NULL;\r
1373     MyLoadSound(&sounds[sc]);\r
1374   }\r
1375 }\r
1376 \r
1377 void\r
1378 SetCommPortDefaults()\r
1379 {\r
1380    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1381   dcb.DCBlength = sizeof(DCB);\r
1382   dcb.BaudRate = 9600;\r
1383   dcb.fBinary = TRUE;\r
1384   dcb.fParity = FALSE;\r
1385   dcb.fOutxCtsFlow = FALSE;\r
1386   dcb.fOutxDsrFlow = FALSE;\r
1387   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1388   dcb.fDsrSensitivity = FALSE;\r
1389   dcb.fTXContinueOnXoff = TRUE;\r
1390   dcb.fOutX = FALSE;\r
1391   dcb.fInX = FALSE;\r
1392   dcb.fNull = FALSE;\r
1393   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1394   dcb.fAbortOnError = FALSE;\r
1395   dcb.ByteSize = 7;\r
1396   dcb.Parity = SPACEPARITY;\r
1397   dcb.StopBits = ONESTOPBIT;\r
1398 }\r
1399 \r
1400 // [HGM] args: these three cases taken out to stay in front-end\r
1401 void\r
1402 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1403 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1404         // while the curent board size determines the element. This system should be ported to XBoard.\r
1405         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1406         int bs;\r
1407         for (bs=0; bs<NUM_SIZES; bs++) {\r
1408           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1409           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1410           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1411             ad->argName, mfp->faceName, mfp->pointSize,\r
1412             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1413             mfp->bold ? "b" : "",\r
1414             mfp->italic ? "i" : "",\r
1415             mfp->underline ? "u" : "",\r
1416             mfp->strikeout ? "s" : "",\r
1417             (int)mfp->charset);\r
1418         }\r
1419       }\r
1420 \r
1421 void\r
1422 ExportSounds()\r
1423 { // [HGM] copy the names from the internal WB variables to appData\r
1424   ColorClass cc;\r
1425   SoundClass sc;\r
1426   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1427     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1428   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1429     (&appData.soundMove)[sc] = sounds[sc].name;\r
1430 }\r
1431 \r
1432 void\r
1433 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1434 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1435         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1436         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1437           (ta->effects & CFE_BOLD) ? "b" : "",\r
1438           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1439           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1440           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1441           (ta->effects) ? " " : "",\r
1442           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1443       }\r
1444 \r
1445 void\r
1446 SaveColor(FILE *f, ArgDescriptor *ad)\r
1447 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1448         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1449         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1450           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1451 }\r
1452 \r
1453 void\r
1454 SaveBoardSize(FILE *f, char *name, void *addr)\r
1455 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1456   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1457 }\r
1458 \r
1459 void\r
1460 ParseCommPortSettings(char *s)\r
1461 { // wrapper to keep dcb from back-end\r
1462   ParseCommSettings(s, &dcb);\r
1463 }\r
1464 \r
1465 void\r
1466 GetWindowCoords()\r
1467 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1468   GetActualPlacement(hwndMain, &wpMain);\r
1469   GetActualPlacement(hwndConsole, &wpConsole);\r
1470   GetActualPlacement(commentDialog, &wpComment);\r
1471   GetActualPlacement(editTagsDialog, &wpTags);\r
1472   GetActualPlacement(gameListDialog, &wpGameList);\r
1473   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1474   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1475   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1476 }\r
1477 \r
1478 void\r
1479 PrintCommPortSettings(FILE *f, char *name)\r
1480 { // wrapper to shield back-end from DCB\r
1481       PrintCommSettings(f, name, &dcb);\r
1482 }\r
1483 \r
1484 int\r
1485 MySearchPath(char *installDir, char *name, char *fullname)\r
1486 {\r
1487   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1488   if(name[0]== '%') {\r
1489     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1490     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1491       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1492       *strchr(buf, '%') = 0;\r
1493       strcat(fullname, getenv(buf));\r
1494       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1495     }\r
1496     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1497     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1498     return (int) strlen(fullname);\r
1499   }\r
1500   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1501 }\r
1502 \r
1503 int\r
1504 MyGetFullPathName(char *name, char *fullname)\r
1505 {\r
1506   char *dummy;\r
1507   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1508 }\r
1509 \r
1510 int\r
1511 MainWindowUp()\r
1512 { // [HGM] args: allows testing if main window is realized from back-end\r
1513   return hwndMain != NULL;\r
1514 }\r
1515 \r
1516 void\r
1517 PopUpStartupDialog()\r
1518 {\r
1519     FARPROC lpProc;\r
1520     \r
1521     LoadLanguageFile(appData.language);\r
1522     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1523     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1524     FreeProcInstance(lpProc);\r
1525 }\r
1526 \r
1527 /*---------------------------------------------------------------------------*\\r
1528  *\r
1529  * GDI board drawing routines\r
1530  *\r
1531 \*---------------------------------------------------------------------------*/\r
1532 \r
1533 /* [AS] Draw square using background texture */\r
1534 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1535 {\r
1536     XFORM   x;\r
1537 \r
1538     if( mode == 0 ) {\r
1539         return; /* Should never happen! */\r
1540     }\r
1541 \r
1542     SetGraphicsMode( dst, GM_ADVANCED );\r
1543 \r
1544     switch( mode ) {\r
1545     case 1:\r
1546         /* Identity */\r
1547         break;\r
1548     case 2:\r
1549         /* X reflection */\r
1550         x.eM11 = -1.0;\r
1551         x.eM12 = 0;\r
1552         x.eM21 = 0;\r
1553         x.eM22 = 1.0;\r
1554         x.eDx = (FLOAT) dw + dx - 1;\r
1555         x.eDy = 0;\r
1556         dx = 0;\r
1557         SetWorldTransform( dst, &x );\r
1558         break;\r
1559     case 3:\r
1560         /* Y reflection */\r
1561         x.eM11 = 1.0;\r
1562         x.eM12 = 0;\r
1563         x.eM21 = 0;\r
1564         x.eM22 = -1.0;\r
1565         x.eDx = 0;\r
1566         x.eDy = (FLOAT) dh + dy - 1;\r
1567         dy = 0;\r
1568         SetWorldTransform( dst, &x );\r
1569         break;\r
1570     case 4:\r
1571         /* X/Y flip */\r
1572         x.eM11 = 0;\r
1573         x.eM12 = 1.0;\r
1574         x.eM21 = 1.0;\r
1575         x.eM22 = 0;\r
1576         x.eDx = (FLOAT) dx;\r
1577         x.eDy = (FLOAT) dy;\r
1578         dx = 0;\r
1579         dy = 0;\r
1580         SetWorldTransform( dst, &x );\r
1581         break;\r
1582     }\r
1583 \r
1584     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1585 \r
1586     x.eM11 = 1.0;\r
1587     x.eM12 = 0;\r
1588     x.eM21 = 0;\r
1589     x.eM22 = 1.0;\r
1590     x.eDx = 0;\r
1591     x.eDy = 0;\r
1592     SetWorldTransform( dst, &x );\r
1593 \r
1594     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1595 }\r
1596 \r
1597 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1598 enum {\r
1599     PM_WP = (int) WhitePawn, \r
1600     PM_WN = (int) WhiteKnight, \r
1601     PM_WB = (int) WhiteBishop, \r
1602     PM_WR = (int) WhiteRook, \r
1603     PM_WQ = (int) WhiteQueen, \r
1604     PM_WF = (int) WhiteFerz, \r
1605     PM_WW = (int) WhiteWazir, \r
1606     PM_WE = (int) WhiteAlfil, \r
1607     PM_WM = (int) WhiteMan, \r
1608     PM_WO = (int) WhiteCannon, \r
1609     PM_WU = (int) WhiteUnicorn, \r
1610     PM_WH = (int) WhiteNightrider, \r
1611     PM_WA = (int) WhiteAngel, \r
1612     PM_WC = (int) WhiteMarshall, \r
1613     PM_WAB = (int) WhiteCardinal, \r
1614     PM_WD = (int) WhiteDragon, \r
1615     PM_WL = (int) WhiteLance, \r
1616     PM_WS = (int) WhiteCobra, \r
1617     PM_WV = (int) WhiteFalcon, \r
1618     PM_WSG = (int) WhiteSilver, \r
1619     PM_WG = (int) WhiteGrasshopper, \r
1620     PM_WK = (int) WhiteKing,\r
1621     PM_BP = (int) BlackPawn, \r
1622     PM_BN = (int) BlackKnight, \r
1623     PM_BB = (int) BlackBishop, \r
1624     PM_BR = (int) BlackRook, \r
1625     PM_BQ = (int) BlackQueen, \r
1626     PM_BF = (int) BlackFerz, \r
1627     PM_BW = (int) BlackWazir, \r
1628     PM_BE = (int) BlackAlfil, \r
1629     PM_BM = (int) BlackMan,\r
1630     PM_BO = (int) BlackCannon, \r
1631     PM_BU = (int) BlackUnicorn, \r
1632     PM_BH = (int) BlackNightrider, \r
1633     PM_BA = (int) BlackAngel, \r
1634     PM_BC = (int) BlackMarshall, \r
1635     PM_BG = (int) BlackGrasshopper, \r
1636     PM_BAB = (int) BlackCardinal,\r
1637     PM_BD = (int) BlackDragon,\r
1638     PM_BL = (int) BlackLance,\r
1639     PM_BS = (int) BlackCobra,\r
1640     PM_BV = (int) BlackFalcon,\r
1641     PM_BSG = (int) BlackSilver,\r
1642     PM_BK = (int) BlackKing\r
1643 };\r
1644 \r
1645 static HFONT hPieceFont = NULL;\r
1646 static HBITMAP hPieceMask[(int) EmptySquare];\r
1647 static HBITMAP hPieceFace[(int) EmptySquare];\r
1648 static int fontBitmapSquareSize = 0;\r
1649 static char pieceToFontChar[(int) EmptySquare] =\r
1650                               { 'p', 'n', 'b', 'r', 'q', \r
1651                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1652                       'k', 'o', 'm', 'v', 't', 'w', \r
1653                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1654                                                               'l' };\r
1655 \r
1656 extern BOOL SetCharTable( char *table, const char * map );\r
1657 /* [HGM] moved to backend.c */\r
1658 \r
1659 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1660 {\r
1661     HBRUSH hbrush;\r
1662     BYTE r1 = GetRValue( color );\r
1663     BYTE g1 = GetGValue( color );\r
1664     BYTE b1 = GetBValue( color );\r
1665     BYTE r2 = r1 / 2;\r
1666     BYTE g2 = g1 / 2;\r
1667     BYTE b2 = b1 / 2;\r
1668     RECT rc;\r
1669 \r
1670     /* Create a uniform background first */\r
1671     hbrush = CreateSolidBrush( color );\r
1672     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1673     FillRect( hdc, &rc, hbrush );\r
1674     DeleteObject( hbrush );\r
1675     \r
1676     if( mode == 1 ) {\r
1677         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1678         int steps = squareSize / 2;\r
1679         int i;\r
1680 \r
1681         for( i=0; i<steps; i++ ) {\r
1682             BYTE r = r1 - (r1-r2) * i / steps;\r
1683             BYTE g = g1 - (g1-g2) * i / steps;\r
1684             BYTE b = b1 - (b1-b2) * i / steps;\r
1685 \r
1686             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1687             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1688             FillRect( hdc, &rc, hbrush );\r
1689             DeleteObject(hbrush);\r
1690         }\r
1691     }\r
1692     else if( mode == 2 ) {\r
1693         /* Diagonal gradient, good more or less for every piece */\r
1694         POINT triangle[3];\r
1695         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1696         HBRUSH hbrush_old;\r
1697         int steps = squareSize;\r
1698         int i;\r
1699 \r
1700         triangle[0].x = squareSize - steps;\r
1701         triangle[0].y = squareSize;\r
1702         triangle[1].x = squareSize;\r
1703         triangle[1].y = squareSize;\r
1704         triangle[2].x = squareSize;\r
1705         triangle[2].y = squareSize - steps;\r
1706 \r
1707         for( i=0; i<steps; i++ ) {\r
1708             BYTE r = r1 - (r1-r2) * i / steps;\r
1709             BYTE g = g1 - (g1-g2) * i / steps;\r
1710             BYTE b = b1 - (b1-b2) * i / steps;\r
1711 \r
1712             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1713             hbrush_old = SelectObject( hdc, hbrush );\r
1714             Polygon( hdc, triangle, 3 );\r
1715             SelectObject( hdc, hbrush_old );\r
1716             DeleteObject(hbrush);\r
1717             triangle[0].x++;\r
1718             triangle[2].y++;\r
1719         }\r
1720 \r
1721         SelectObject( hdc, hpen );\r
1722     }\r
1723 }\r
1724 \r
1725 /*\r
1726     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1727     seems to work ok. The main problem here is to find the "inside" of a chess\r
1728     piece: follow the steps as explained below.\r
1729 */\r
1730 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1731 {\r
1732     HBITMAP hbm;\r
1733     HBITMAP hbm_old;\r
1734     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1735     RECT rc;\r
1736     SIZE sz;\r
1737     POINT pt;\r
1738     int backColor = whitePieceColor; \r
1739     int foreColor = blackPieceColor;\r
1740     \r
1741     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1742         backColor = appData.fontBackColorWhite;\r
1743         foreColor = appData.fontForeColorWhite;\r
1744     }\r
1745     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1746         backColor = appData.fontBackColorBlack;\r
1747         foreColor = appData.fontForeColorBlack;\r
1748     }\r
1749 \r
1750     /* Mask */\r
1751     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1752 \r
1753     hbm_old = SelectObject( hdc, hbm );\r
1754 \r
1755     rc.left = 0;\r
1756     rc.top = 0;\r
1757     rc.right = squareSize;\r
1758     rc.bottom = squareSize;\r
1759 \r
1760     /* Step 1: background is now black */\r
1761     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1762 \r
1763     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1764 \r
1765     pt.x = (squareSize - sz.cx) / 2;\r
1766     pt.y = (squareSize - sz.cy) / 2;\r
1767 \r
1768     SetBkMode( hdc, TRANSPARENT );\r
1769     SetTextColor( hdc, chroma );\r
1770     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1771     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1772 \r
1773     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1774     /* Step 3: the area outside the piece is filled with white */\r
1775 //    FloodFill( hdc, 0, 0, chroma );\r
1776     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1777     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1778     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1779     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1780     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1781     /* \r
1782         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1783         but if the start point is not inside the piece we're lost!\r
1784         There should be a better way to do this... if we could create a region or path\r
1785         from the fill operation we would be fine for example.\r
1786     */\r
1787 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1788     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1789 \r
1790     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1791         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1792         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1793 \r
1794         SelectObject( dc2, bm2 );\r
1795         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1796         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1797         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1798         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1799         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1800 \r
1801         DeleteDC( dc2 );\r
1802         DeleteObject( bm2 );\r
1803     }\r
1804 \r
1805     SetTextColor( hdc, 0 );\r
1806     /* \r
1807         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1808         draw the piece again in black for safety.\r
1809     */\r
1810     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1811 \r
1812     SelectObject( hdc, hbm_old );\r
1813 \r
1814     if( hPieceMask[index] != NULL ) {\r
1815         DeleteObject( hPieceMask[index] );\r
1816     }\r
1817 \r
1818     hPieceMask[index] = hbm;\r
1819 \r
1820     /* Face */\r
1821     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1822 \r
1823     SelectObject( hdc, hbm );\r
1824 \r
1825     {\r
1826         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1827         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1828         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1829 \r
1830         SelectObject( dc1, hPieceMask[index] );\r
1831         SelectObject( dc2, bm2 );\r
1832         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1833         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1834         \r
1835         /* \r
1836             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1837             the piece background and deletes (makes transparent) the rest.\r
1838             Thanks to that mask, we are free to paint the background with the greates\r
1839             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1840             We use this, to make gradients and give the pieces a "roundish" look.\r
1841         */\r
1842         SetPieceBackground( hdc, backColor, 2 );\r
1843         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1844 \r
1845         DeleteDC( dc2 );\r
1846         DeleteDC( dc1 );\r
1847         DeleteObject( bm2 );\r
1848     }\r
1849 \r
1850     SetTextColor( hdc, foreColor );\r
1851     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1852 \r
1853     SelectObject( hdc, hbm_old );\r
1854 \r
1855     if( hPieceFace[index] != NULL ) {\r
1856         DeleteObject( hPieceFace[index] );\r
1857     }\r
1858 \r
1859     hPieceFace[index] = hbm;\r
1860 }\r
1861 \r
1862 static int TranslatePieceToFontPiece( int piece )\r
1863 {\r
1864     switch( piece ) {\r
1865     case BlackPawn:\r
1866         return PM_BP;\r
1867     case BlackKnight:\r
1868         return PM_BN;\r
1869     case BlackBishop:\r
1870         return PM_BB;\r
1871     case BlackRook:\r
1872         return PM_BR;\r
1873     case BlackQueen:\r
1874         return PM_BQ;\r
1875     case BlackKing:\r
1876         return PM_BK;\r
1877     case WhitePawn:\r
1878         return PM_WP;\r
1879     case WhiteKnight:\r
1880         return PM_WN;\r
1881     case WhiteBishop:\r
1882         return PM_WB;\r
1883     case WhiteRook:\r
1884         return PM_WR;\r
1885     case WhiteQueen:\r
1886         return PM_WQ;\r
1887     case WhiteKing:\r
1888         return PM_WK;\r
1889 \r
1890     case BlackAngel:\r
1891         return PM_BA;\r
1892     case BlackMarshall:\r
1893         return PM_BC;\r
1894     case BlackFerz:\r
1895         return PM_BF;\r
1896     case BlackNightrider:\r
1897         return PM_BH;\r
1898     case BlackAlfil:\r
1899         return PM_BE;\r
1900     case BlackWazir:\r
1901         return PM_BW;\r
1902     case BlackUnicorn:\r
1903         return PM_BU;\r
1904     case BlackCannon:\r
1905         return PM_BO;\r
1906     case BlackGrasshopper:\r
1907         return PM_BG;\r
1908     case BlackMan:\r
1909         return PM_BM;\r
1910     case BlackSilver:\r
1911         return PM_BSG;\r
1912     case BlackLance:\r
1913         return PM_BL;\r
1914     case BlackFalcon:\r
1915         return PM_BV;\r
1916     case BlackCobra:\r
1917         return PM_BS;\r
1918     case BlackCardinal:\r
1919         return PM_BAB;\r
1920     case BlackDragon:\r
1921         return PM_BD;\r
1922 \r
1923     case WhiteAngel:\r
1924         return PM_WA;\r
1925     case WhiteMarshall:\r
1926         return PM_WC;\r
1927     case WhiteFerz:\r
1928         return PM_WF;\r
1929     case WhiteNightrider:\r
1930         return PM_WH;\r
1931     case WhiteAlfil:\r
1932         return PM_WE;\r
1933     case WhiteWazir:\r
1934         return PM_WW;\r
1935     case WhiteUnicorn:\r
1936         return PM_WU;\r
1937     case WhiteCannon:\r
1938         return PM_WO;\r
1939     case WhiteGrasshopper:\r
1940         return PM_WG;\r
1941     case WhiteMan:\r
1942         return PM_WM;\r
1943     case WhiteSilver:\r
1944         return PM_WSG;\r
1945     case WhiteLance:\r
1946         return PM_WL;\r
1947     case WhiteFalcon:\r
1948         return PM_WV;\r
1949     case WhiteCobra:\r
1950         return PM_WS;\r
1951     case WhiteCardinal:\r
1952         return PM_WAB;\r
1953     case WhiteDragon:\r
1954         return PM_WD;\r
1955     }\r
1956 \r
1957     return 0;\r
1958 }\r
1959 \r
1960 void CreatePiecesFromFont()\r
1961 {\r
1962     LOGFONT lf;\r
1963     HDC hdc_window = NULL;\r
1964     HDC hdc = NULL;\r
1965     HFONT hfont_old;\r
1966     int fontHeight;\r
1967     int i;\r
1968 \r
1969     if( fontBitmapSquareSize < 0 ) {\r
1970         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1971         return;\r
1972     }\r
1973 \r
1974     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1975         fontBitmapSquareSize = -1;\r
1976         return;\r
1977     }\r
1978 \r
1979     if( fontBitmapSquareSize != squareSize ) {\r
1980         hdc_window = GetDC( hwndMain );\r
1981         hdc = CreateCompatibleDC( hdc_window );\r
1982 \r
1983         if( hPieceFont != NULL ) {\r
1984             DeleteObject( hPieceFont );\r
1985         }\r
1986         else {\r
1987             for( i=0; i<=(int)BlackKing; i++ ) {\r
1988                 hPieceMask[i] = NULL;\r
1989                 hPieceFace[i] = NULL;\r
1990             }\r
1991         }\r
1992 \r
1993         fontHeight = 75;\r
1994 \r
1995         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1996             fontHeight = appData.fontPieceSize;\r
1997         }\r
1998 \r
1999         fontHeight = (fontHeight * squareSize) / 100;\r
2000 \r
2001         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2002         lf.lfWidth = 0;\r
2003         lf.lfEscapement = 0;\r
2004         lf.lfOrientation = 0;\r
2005         lf.lfWeight = FW_NORMAL;\r
2006         lf.lfItalic = 0;\r
2007         lf.lfUnderline = 0;\r
2008         lf.lfStrikeOut = 0;\r
2009         lf.lfCharSet = DEFAULT_CHARSET;\r
2010         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2011         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2012         lf.lfQuality = PROOF_QUALITY;\r
2013         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2014         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2015         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2016 \r
2017         hPieceFont = CreateFontIndirect( &lf );\r
2018 \r
2019         if( hPieceFont == NULL ) {\r
2020             fontBitmapSquareSize = -2;\r
2021         }\r
2022         else {\r
2023             /* Setup font-to-piece character table */\r
2024             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2025                 /* No (or wrong) global settings, try to detect the font */\r
2026                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2027                     /* Alpha */\r
2028                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2029                 }\r
2030                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2031                     /* DiagramTT* family */\r
2032                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2033                 }\r
2034                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2035                     /* Fairy symbols */\r
2036                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2037                 }\r
2038                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2039                     /* Good Companion (Some characters get warped as literal :-( */\r
2040                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2041                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2042                     SetCharTable(pieceToFontChar, s);\r
2043                 }\r
2044                 else {\r
2045                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2046                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2047                 }\r
2048             }\r
2049 \r
2050             /* Create bitmaps */\r
2051             hfont_old = SelectObject( hdc, hPieceFont );\r
2052             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2053                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2054                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2055 \r
2056             SelectObject( hdc, hfont_old );\r
2057 \r
2058             fontBitmapSquareSize = squareSize;\r
2059         }\r
2060     }\r
2061 \r
2062     if( hdc != NULL ) {\r
2063         DeleteDC( hdc );\r
2064     }\r
2065 \r
2066     if( hdc_window != NULL ) {\r
2067         ReleaseDC( hwndMain, hdc_window );\r
2068     }\r
2069 }\r
2070 \r
2071 HBITMAP\r
2072 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2073 {\r
2074   char name[128];\r
2075 \r
2076     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2077   if (gameInfo.event &&\r
2078       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2079       strcmp(name, "k80s") == 0) {\r
2080     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2081   }\r
2082   return LoadBitmap(hinst, name);\r
2083 }\r
2084 \r
2085 \r
2086 /* Insert a color into the program's logical palette\r
2087    structure.  This code assumes the given color is\r
2088    the result of the RGB or PALETTERGB macro, and it\r
2089    knows how those macros work (which is documented).\r
2090 */\r
2091 VOID\r
2092 InsertInPalette(COLORREF color)\r
2093 {\r
2094   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2095 \r
2096   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2097     DisplayFatalError(_("Too many colors"), 0, 1);\r
2098     pLogPal->palNumEntries--;\r
2099     return;\r
2100   }\r
2101 \r
2102   pe->peFlags = (char) 0;\r
2103   pe->peRed = (char) (0xFF & color);\r
2104   pe->peGreen = (char) (0xFF & (color >> 8));\r
2105   pe->peBlue = (char) (0xFF & (color >> 16));\r
2106   return;\r
2107 }\r
2108 \r
2109 \r
2110 VOID\r
2111 InitDrawingColors()\r
2112 {\r
2113   if (pLogPal == NULL) {\r
2114     /* Allocate enough memory for a logical palette with\r
2115      * PALETTESIZE entries and set the size and version fields\r
2116      * of the logical palette structure.\r
2117      */\r
2118     pLogPal = (NPLOGPALETTE)\r
2119       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2120                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2121     pLogPal->palVersion    = 0x300;\r
2122   }\r
2123   pLogPal->palNumEntries = 0;\r
2124 \r
2125   InsertInPalette(lightSquareColor);\r
2126   InsertInPalette(darkSquareColor);\r
2127   InsertInPalette(whitePieceColor);\r
2128   InsertInPalette(blackPieceColor);\r
2129   InsertInPalette(highlightSquareColor);\r
2130   InsertInPalette(premoveHighlightColor);\r
2131 \r
2132   /*  create a logical color palette according the information\r
2133    *  in the LOGPALETTE structure.\r
2134    */\r
2135   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2136 \r
2137   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2138   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2139   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2140   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2141   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2142   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2143   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2144   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2145   /* [AS] Force rendering of the font-based pieces */\r
2146   if( fontBitmapSquareSize > 0 ) {\r
2147     fontBitmapSquareSize = 0;\r
2148   }\r
2149 }\r
2150 \r
2151 \r
2152 int\r
2153 BoardWidth(int boardSize, int n)\r
2154 { /* [HGM] argument n added to allow different width and height */\r
2155   int lineGap = sizeInfo[boardSize].lineGap;\r
2156 \r
2157   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2158       lineGap = appData.overrideLineGap;\r
2159   }\r
2160 \r
2161   return (n + 1) * lineGap +\r
2162           n * sizeInfo[boardSize].squareSize;\r
2163 }\r
2164 \r
2165 /* Respond to board resize by dragging edge */\r
2166 VOID\r
2167 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2168 {\r
2169   BoardSize newSize = NUM_SIZES - 1;\r
2170   static int recurse = 0;\r
2171   if (IsIconic(hwndMain)) return;\r
2172   if (recurse > 0) return;\r
2173   recurse++;\r
2174   while (newSize > 0) {\r
2175         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2176         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2177            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2178     newSize--;\r
2179   } \r
2180   boardSize = newSize;\r
2181   InitDrawingSizes(boardSize, flags);\r
2182   recurse--;\r
2183 }\r
2184 \r
2185 \r
2186 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2187 \r
2188 VOID\r
2189 InitDrawingSizes(BoardSize boardSize, int flags)\r
2190 {\r
2191   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2192   ChessSquare piece;\r
2193   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2194   HDC hdc;\r
2195   SIZE clockSize, messageSize;\r
2196   HFONT oldFont;\r
2197   char buf[MSG_SIZ];\r
2198   char *str;\r
2199   HMENU hmenu = GetMenu(hwndMain);\r
2200   RECT crect, wrect, oldRect;\r
2201   int offby;\r
2202   LOGBRUSH logbrush;\r
2203 \r
2204   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2205   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2206 \r
2207   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2208   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2209 \r
2210   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2211   oldRect.top = wpMain.y;\r
2212   oldRect.right = wpMain.x + wpMain.width;\r
2213   oldRect.bottom = wpMain.y + wpMain.height;\r
2214 \r
2215   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2216   smallLayout = sizeInfo[boardSize].smallLayout;\r
2217   squareSize = sizeInfo[boardSize].squareSize;\r
2218   lineGap = sizeInfo[boardSize].lineGap;\r
2219   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2220 \r
2221   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2222       lineGap = appData.overrideLineGap;\r
2223   }\r
2224 \r
2225   if (tinyLayout != oldTinyLayout) {\r
2226     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2227     if (tinyLayout) {\r
2228       style &= ~WS_SYSMENU;\r
2229       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2230                  "&Minimize\tCtrl+F4");\r
2231     } else {\r
2232       style |= WS_SYSMENU;\r
2233       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2234     }\r
2235     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2236 \r
2237     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2238       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2239         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2240     }\r
2241     DrawMenuBar(hwndMain);\r
2242   }\r
2243 \r
2244   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2245   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2246 \r
2247   /* Get text area sizes */\r
2248   hdc = GetDC(hwndMain);\r
2249   if (appData.clockMode) {\r
2250     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2251   } else {\r
2252     snprintf(buf, MSG_SIZ, _("White"));\r
2253   }\r
2254   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2255   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2256   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2257   str = _("We only care about the height here");\r
2258   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2259   SelectObject(hdc, oldFont);\r
2260   ReleaseDC(hwndMain, hdc);\r
2261 \r
2262   /* Compute where everything goes */\r
2263   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2264         /* [HGM] logo: if either logo is on, reserve space for it */\r
2265         logoHeight =  2*clockSize.cy;\r
2266         leftLogoRect.left   = OUTER_MARGIN;\r
2267         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2268         leftLogoRect.top    = OUTER_MARGIN;\r
2269         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2270 \r
2271         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2272         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2273         rightLogoRect.top    = OUTER_MARGIN;\r
2274         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2275 \r
2276 \r
2277     whiteRect.left = leftLogoRect.right;\r
2278     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2279     whiteRect.top = OUTER_MARGIN;\r
2280     whiteRect.bottom = whiteRect.top + logoHeight;\r
2281 \r
2282     blackRect.right = rightLogoRect.left;\r
2283     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2284     blackRect.top = whiteRect.top;\r
2285     blackRect.bottom = whiteRect.bottom;\r
2286   } else {\r
2287     whiteRect.left = OUTER_MARGIN;\r
2288     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2289     whiteRect.top = OUTER_MARGIN;\r
2290     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2291 \r
2292     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2293     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2294     blackRect.top = whiteRect.top;\r
2295     blackRect.bottom = whiteRect.bottom;\r
2296 \r
2297     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2298   }\r
2299 \r
2300   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2301   if (appData.showButtonBar) {\r
2302     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2303       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2304   } else {\r
2305     messageRect.right = OUTER_MARGIN + boardWidth;\r
2306   }\r
2307   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2308   messageRect.bottom = messageRect.top + messageSize.cy;\r
2309 \r
2310   boardRect.left = OUTER_MARGIN;\r
2311   boardRect.right = boardRect.left + boardWidth;\r
2312   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2313   boardRect.bottom = boardRect.top + boardHeight;\r
2314 \r
2315   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2316   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2317   oldBoardSize = boardSize;\r
2318   oldTinyLayout = tinyLayout;\r
2319   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2320   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2321     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2322   winW *= 1 + twoBoards;\r
2323   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2324   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2325   wpMain.height = winH; //       without disturbing window attachments\r
2326   GetWindowRect(hwndMain, &wrect);\r
2327   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2328                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2329 \r
2330   // [HGM] placement: let attached windows follow size change.\r
2331   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2332   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2333   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2334   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2335   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2336 \r
2337   /* compensate if menu bar wrapped */\r
2338   GetClientRect(hwndMain, &crect);\r
2339   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2340   wpMain.height += offby;\r
2341   switch (flags) {\r
2342   case WMSZ_TOPLEFT:\r
2343     SetWindowPos(hwndMain, NULL, \r
2344                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2345                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2346     break;\r
2347 \r
2348   case WMSZ_TOPRIGHT:\r
2349   case WMSZ_TOP:\r
2350     SetWindowPos(hwndMain, NULL, \r
2351                  wrect.left, wrect.bottom - wpMain.height, \r
2352                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2353     break;\r
2354 \r
2355   case WMSZ_BOTTOMLEFT:\r
2356   case WMSZ_LEFT:\r
2357     SetWindowPos(hwndMain, NULL, \r
2358                  wrect.right - wpMain.width, wrect.top, \r
2359                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2360     break;\r
2361 \r
2362   case WMSZ_BOTTOMRIGHT:\r
2363   case WMSZ_BOTTOM:\r
2364   case WMSZ_RIGHT:\r
2365   default:\r
2366     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2367                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2368     break;\r
2369   }\r
2370 \r
2371   hwndPause = NULL;\r
2372   for (i = 0; i < N_BUTTONS; i++) {\r
2373     if (buttonDesc[i].hwnd != NULL) {\r
2374       DestroyWindow(buttonDesc[i].hwnd);\r
2375       buttonDesc[i].hwnd = NULL;\r
2376     }\r
2377     if (appData.showButtonBar) {\r
2378       buttonDesc[i].hwnd =\r
2379         CreateWindow("BUTTON", buttonDesc[i].label,\r
2380                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2381                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2382                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2383                      (HMENU) buttonDesc[i].id,\r
2384                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2385       if (tinyLayout) {\r
2386         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2387                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2388                     MAKELPARAM(FALSE, 0));\r
2389       }\r
2390       if (buttonDesc[i].id == IDM_Pause)\r
2391         hwndPause = buttonDesc[i].hwnd;\r
2392       buttonDesc[i].wndproc = (WNDPROC)\r
2393         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2394     }\r
2395   }\r
2396   if (gridPen != NULL) DeleteObject(gridPen);\r
2397   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2398   if (premovePen != NULL) DeleteObject(premovePen);\r
2399   if (lineGap != 0) {\r
2400     logbrush.lbStyle = BS_SOLID;\r
2401     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2402     gridPen =\r
2403       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2404                    lineGap, &logbrush, 0, NULL);\r
2405     logbrush.lbColor = highlightSquareColor;\r
2406     highlightPen =\r
2407       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2408                    lineGap, &logbrush, 0, NULL);\r
2409 \r
2410     logbrush.lbColor = premoveHighlightColor; \r
2411     premovePen =\r
2412       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2413                    lineGap, &logbrush, 0, NULL);\r
2414 \r
2415     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2416     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2417       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2418       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2419         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2420       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2421         BOARD_WIDTH * (squareSize + lineGap);\r
2422       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2423     }\r
2424     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2425       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2426       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2427         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2428         lineGap / 2 + (i * (squareSize + lineGap));\r
2429       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2430         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2431       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2432     }\r
2433   }\r
2434 \r
2435   /* [HGM] Licensing requirement */\r
2436 #ifdef GOTHIC\r
2437   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2438 #endif\r
2439 #ifdef FALCON\r
2440   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2441 #endif\r
2442   GothicPopUp( "", VariantNormal);\r
2443 \r
2444 \r
2445 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2446 \r
2447   /* Load piece bitmaps for this board size */\r
2448   for (i=0; i<=2; i++) {\r
2449     for (piece = WhitePawn;\r
2450          (int) piece < (int) BlackPawn;\r
2451          piece = (ChessSquare) ((int) piece + 1)) {\r
2452       if (pieceBitmap[i][piece] != NULL)\r
2453         DeleteObject(pieceBitmap[i][piece]);\r
2454     }\r
2455   }\r
2456 \r
2457   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2458   // Orthodox Chess pieces\r
2459   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2460   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2461   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2462   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2463   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2464   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2465   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2466   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2467   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2468   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2469   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2470   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2471   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2472   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2473   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2474   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2475     // in Shogi, Hijack the unused Queen for Lance\r
2476     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2477     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2478     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2479   } else {\r
2480     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2481     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2482     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2483   }\r
2484 \r
2485   if(squareSize <= 72 && squareSize >= 33) { \r
2486     /* A & C are available in most sizes now */\r
2487     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2488       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2489       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2490       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2491       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2492       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2493       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2494       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2495       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2496       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2497       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2498       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2499       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2500     } else { // Smirf-like\r
2501       if(gameInfo.variant == VariantSChess) {\r
2502         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2503         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2504         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2505       } else {\r
2506         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2507         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2508         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2509       }\r
2510     }\r
2511     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2512       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2513       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2514       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2515     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2516       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2517       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2518       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2519     } else { // WinBoard standard\r
2520       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2521       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2522       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2523     }\r
2524   }\r
2525 \r
2526 \r
2527   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2528     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2529     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2530     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2531     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2532     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2533     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2534     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2535     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2536     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2537     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2538     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2539     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2540     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2541     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2542     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2543     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2544     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2545     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2546     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2547     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2548     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2549     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2550     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2551     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2552     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2553     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2554     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2555     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2556     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2557     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2558 \r
2559     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2560       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2561       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2562       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2563       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2564       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2565       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2566       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2567       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2568       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2569       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2570       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2571       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2572     } else {\r
2573       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2574       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2575       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2576       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2577       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2578       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2579       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2580       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2581       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2582       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2583       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2584       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2585     }\r
2586 \r
2587   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2588     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2589     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2590     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2591     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2592     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2593     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2594     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2595     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2596     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2597     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2598     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2599     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2600     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2601     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2602   }\r
2603 \r
2604 \r
2605   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2606   /* special Shogi support in this size */\r
2607   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2608       for (piece = WhitePawn;\r
2609            (int) piece < (int) BlackPawn;\r
2610            piece = (ChessSquare) ((int) piece + 1)) {\r
2611         if (pieceBitmap[i][piece] != NULL)\r
2612           DeleteObject(pieceBitmap[i][piece]);\r
2613       }\r
2614     }\r
2615   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2616   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2617   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2618   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2619   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2620   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2621   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2622   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2623   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2624   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2625   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2626   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2627   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2628   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2629   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2630   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2631   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2632   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2633   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2634   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2635   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2636   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2637   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2638   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2639   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2640   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2641   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2642   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2643   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2644   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2645   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2646   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2647   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2648   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2649   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2650   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2651   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2652   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2653   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2654   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2655   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2656   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2657   minorSize = 0;\r
2658   }\r
2659 }\r
2660 \r
2661 HBITMAP\r
2662 PieceBitmap(ChessSquare p, int kind)\r
2663 {\r
2664   if ((int) p >= (int) BlackPawn)\r
2665     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2666 \r
2667   return pieceBitmap[kind][(int) p];\r
2668 }\r
2669 \r
2670 /***************************************************************/\r
2671 \r
2672 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2673 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2674 /*\r
2675 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2676 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2677 */\r
2678 \r
2679 VOID\r
2680 SquareToPos(int row, int column, int * x, int * y)\r
2681 {\r
2682   if (flipView) {\r
2683     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2684     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2685   } else {\r
2686     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2687     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2688   }\r
2689 }\r
2690 \r
2691 VOID\r
2692 DrawCoordsOnDC(HDC hdc)\r
2693 {\r
2694   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
2695   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
2696   char str[2] = { NULLCHAR, NULLCHAR };\r
2697   int oldMode, oldAlign, x, y, start, i;\r
2698   HFONT oldFont;\r
2699   HBRUSH oldBrush;\r
2700 \r
2701   if (!appData.showCoords)\r
2702     return;\r
2703 \r
2704   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2705 \r
2706   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2707   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2708   oldAlign = GetTextAlign(hdc);\r
2709   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2710 \r
2711   y = boardRect.top + lineGap;\r
2712   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2713 \r
2714   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2715   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2716     str[0] = files[start + i];\r
2717     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2718     y += squareSize + lineGap;\r
2719   }\r
2720 \r
2721   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2722 \r
2723   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2724   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2725     str[0] = ranks[start + i];\r
2726     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2727     x += squareSize + lineGap;\r
2728   }    \r
2729 \r
2730   SelectObject(hdc, oldBrush);\r
2731   SetBkMode(hdc, oldMode);\r
2732   SetTextAlign(hdc, oldAlign);\r
2733   SelectObject(hdc, oldFont);\r
2734 }\r
2735 \r
2736 VOID\r
2737 DrawGridOnDC(HDC hdc)\r
2738 {\r
2739   HPEN oldPen;\r
2740  \r
2741   if (lineGap != 0) {\r
2742     oldPen = SelectObject(hdc, gridPen);\r
2743     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2744     SelectObject(hdc, oldPen);\r
2745   }\r
2746 }\r
2747 \r
2748 #define HIGHLIGHT_PEN 0\r
2749 #define PREMOVE_PEN   1\r
2750 \r
2751 VOID\r
2752 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2753 {\r
2754   int x1, y1;\r
2755   HPEN oldPen, hPen;\r
2756   if (lineGap == 0) return;\r
2757   if (flipView) {\r
2758     x1 = boardRect.left +\r
2759       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2760     y1 = boardRect.top +\r
2761       lineGap/2 + y * (squareSize + lineGap);\r
2762   } else {\r
2763     x1 = boardRect.left +\r
2764       lineGap/2 + x * (squareSize + lineGap);\r
2765     y1 = boardRect.top +\r
2766       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2767   }\r
2768   hPen = pen ? premovePen : highlightPen;\r
2769   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2770   MoveToEx(hdc, x1, y1, NULL);\r
2771   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2772   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2773   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2774   LineTo(hdc, x1, y1);\r
2775   SelectObject(hdc, oldPen);\r
2776 }\r
2777 \r
2778 VOID\r
2779 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2780 {\r
2781   int i;\r
2782   for (i=0; i<2; i++) {\r
2783     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2784       DrawHighlightOnDC(hdc, TRUE,\r
2785                         h->sq[i].x, h->sq[i].y,\r
2786                         pen);\r
2787   }\r
2788 }\r
2789 \r
2790 /* Note: sqcolor is used only in monoMode */\r
2791 /* Note that this code is largely duplicated in woptions.c,\r
2792    function DrawSampleSquare, so that needs to be updated too */\r
2793 VOID\r
2794 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2795 {\r
2796   HBITMAP oldBitmap;\r
2797   HBRUSH oldBrush;\r
2798   int tmpSize;\r
2799 \r
2800   if (appData.blindfold) return;\r
2801 \r
2802   /* [AS] Use font-based pieces if needed */\r
2803   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2804     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2805     CreatePiecesFromFont();\r
2806 \r
2807     if( fontBitmapSquareSize == squareSize ) {\r
2808         int index = TranslatePieceToFontPiece(piece);\r
2809 \r
2810         SelectObject( tmphdc, hPieceMask[ index ] );\r
2811 \r
2812       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2813         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2814       else\r
2815         BitBlt( hdc,\r
2816             x, y,\r
2817             squareSize, squareSize,\r
2818             tmphdc,\r
2819             0, 0,\r
2820             SRCAND );\r
2821 \r
2822         SelectObject( tmphdc, hPieceFace[ index ] );\r
2823 \r
2824       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2825         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2826       else\r
2827         BitBlt( hdc,\r
2828             x, y,\r
2829             squareSize, squareSize,\r
2830             tmphdc,\r
2831             0, 0,\r
2832             SRCPAINT );\r
2833 \r
2834         return;\r
2835     }\r
2836   }\r
2837 \r
2838   if (appData.monoMode) {\r
2839     SelectObject(tmphdc, PieceBitmap(piece, \r
2840       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2841     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2842            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2843   } else {\r
2844     tmpSize = squareSize;\r
2845     if(minorSize &&\r
2846         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2847          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2848       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2849       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2850       x += (squareSize - minorSize)>>1;\r
2851       y += squareSize - minorSize - 2;\r
2852       tmpSize = minorSize;\r
2853     }\r
2854     if (color || appData.allWhite ) {\r
2855       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2856       if( color )\r
2857               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2858       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2859       if(appData.upsideDown && color==flipView)\r
2860         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2861       else\r
2862         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2863       /* Use black for outline of white pieces */\r
2864       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2865       if(appData.upsideDown && color==flipView)\r
2866         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2867       else\r
2868         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2869     } else {\r
2870       /* Use square color for details of black pieces */\r
2871       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2872       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2873       if(appData.upsideDown && !flipView)\r
2874         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2875       else\r
2876         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2877     }\r
2878     SelectObject(hdc, oldBrush);\r
2879     SelectObject(tmphdc, oldBitmap);\r
2880   }\r
2881 }\r
2882 \r
2883 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2884 int GetBackTextureMode( int algo )\r
2885 {\r
2886     int result = BACK_TEXTURE_MODE_DISABLED;\r
2887 \r
2888     switch( algo ) \r
2889     {\r
2890         case BACK_TEXTURE_MODE_PLAIN:\r
2891             result = 1; /* Always use identity map */\r
2892             break;\r
2893         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2894             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2895             break;\r
2896     }\r
2897 \r
2898     return result;\r
2899 }\r
2900 \r
2901 /* \r
2902     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2903     to handle redraws cleanly (as random numbers would always be different).\r
2904 */\r
2905 VOID RebuildTextureSquareInfo()\r
2906 {\r
2907     BITMAP bi;\r
2908     int lite_w = 0;\r
2909     int lite_h = 0;\r
2910     int dark_w = 0;\r
2911     int dark_h = 0;\r
2912     int row;\r
2913     int col;\r
2914 \r
2915     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2916 \r
2917     if( liteBackTexture != NULL ) {\r
2918         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2919             lite_w = bi.bmWidth;\r
2920             lite_h = bi.bmHeight;\r
2921         }\r
2922     }\r
2923 \r
2924     if( darkBackTexture != NULL ) {\r
2925         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2926             dark_w = bi.bmWidth;\r
2927             dark_h = bi.bmHeight;\r
2928         }\r
2929     }\r
2930 \r
2931     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2932         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2933             if( (col + row) & 1 ) {\r
2934                 /* Lite square */\r
2935                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2936                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2937                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2938                   else\r
2939                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2940                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2941                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2942                   else\r
2943                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2944                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2945                 }\r
2946             }\r
2947             else {\r
2948                 /* Dark square */\r
2949                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2950                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2951                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2952                   else\r
2953                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2954                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2955                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2956                   else\r
2957                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2958                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2959                 }\r
2960             }\r
2961         }\r
2962     }\r
2963 }\r
2964 \r
2965 /* [AS] Arrow highlighting support */\r
2966 \r
2967 static double A_WIDTH = 5; /* Width of arrow body */\r
2968 \r
2969 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2970 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2971 \r
2972 static double Sqr( double x )\r
2973 {\r
2974     return x*x;\r
2975 }\r
2976 \r
2977 static int Round( double x )\r
2978 {\r
2979     return (int) (x + 0.5);\r
2980 }\r
2981 \r
2982 /* Draw an arrow between two points using current settings */\r
2983 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2984 {\r
2985     POINT arrow[7];\r
2986     double dx, dy, j, k, x, y;\r
2987 \r
2988     if( d_x == s_x ) {\r
2989         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2990 \r
2991         arrow[0].x = s_x + A_WIDTH + 0.5;\r
2992         arrow[0].y = s_y;\r
2993 \r
2994         arrow[1].x = s_x + A_WIDTH + 0.5;\r
2995         arrow[1].y = d_y - h;\r
2996 \r
2997         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2998         arrow[2].y = d_y - h;\r
2999 \r
3000         arrow[3].x = d_x;\r
3001         arrow[3].y = d_y;\r
3002 \r
3003         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3004         arrow[5].y = d_y - h;\r
3005 \r
3006         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3007         arrow[4].y = d_y - h;\r
3008 \r
3009         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3010         arrow[6].y = s_y;\r
3011     }\r
3012     else if( d_y == s_y ) {\r
3013         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3014 \r
3015         arrow[0].x = s_x;\r
3016         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3017 \r
3018         arrow[1].x = d_x - w;\r
3019         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3020 \r
3021         arrow[2].x = d_x - w;\r
3022         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3023 \r
3024         arrow[3].x = d_x;\r
3025         arrow[3].y = d_y;\r
3026 \r
3027         arrow[5].x = d_x - w;\r
3028         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3029 \r
3030         arrow[4].x = d_x - w;\r
3031         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3032 \r
3033         arrow[6].x = s_x;\r
3034         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3035     }\r
3036     else {\r
3037         /* [AS] Needed a lot of paper for this! :-) */\r
3038         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3039         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3040   \r
3041         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3042 \r
3043         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3044 \r
3045         x = s_x;\r
3046         y = s_y;\r
3047 \r
3048         arrow[0].x = Round(x - j);\r
3049         arrow[0].y = Round(y + j*dx);\r
3050 \r
3051         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3052         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3053 \r
3054         if( d_x > s_x ) {\r
3055             x = (double) d_x - k;\r
3056             y = (double) d_y - k*dy;\r
3057         }\r
3058         else {\r
3059             x = (double) d_x + k;\r
3060             y = (double) d_y + k*dy;\r
3061         }\r
3062 \r
3063         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3064 \r
3065         arrow[6].x = Round(x - j);\r
3066         arrow[6].y = Round(y + j*dx);\r
3067 \r
3068         arrow[2].x = Round(arrow[6].x + 2*j);\r
3069         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3070 \r
3071         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3072         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3073 \r
3074         arrow[4].x = d_x;\r
3075         arrow[4].y = d_y;\r
3076 \r
3077         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3078         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3079     }\r
3080 \r
3081     Polygon( hdc, arrow, 7 );\r
3082 }\r
3083 \r
3084 /* [AS] Draw an arrow between two squares */\r
3085 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3086 {\r
3087     int s_x, s_y, d_x, d_y;\r
3088     HPEN hpen;\r
3089     HPEN holdpen;\r
3090     HBRUSH hbrush;\r
3091     HBRUSH holdbrush;\r
3092     LOGBRUSH stLB;\r
3093 \r
3094     if( s_col == d_col && s_row == d_row ) {\r
3095         return;\r
3096     }\r
3097 \r
3098     /* Get source and destination points */\r
3099     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3100     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3101 \r
3102     if( d_y > s_y ) {\r
3103         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3104     }\r
3105     else if( d_y < s_y ) {\r
3106         d_y += squareSize / 2 + squareSize / 4;\r
3107     }\r
3108     else {\r
3109         d_y += squareSize / 2;\r
3110     }\r
3111 \r
3112     if( d_x > s_x ) {\r
3113         d_x += squareSize / 2 - squareSize / 4;\r
3114     }\r
3115     else if( d_x < s_x ) {\r
3116         d_x += squareSize / 2 + squareSize / 4;\r
3117     }\r
3118     else {\r
3119         d_x += squareSize / 2;\r
3120     }\r
3121 \r
3122     s_x += squareSize / 2;\r
3123     s_y += squareSize / 2;\r
3124 \r
3125     /* Adjust width */\r
3126     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3127 \r
3128     /* Draw */\r
3129     stLB.lbStyle = BS_SOLID;\r
3130     stLB.lbColor = appData.highlightArrowColor;\r
3131     stLB.lbHatch = 0;\r
3132 \r
3133     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3134     holdpen = SelectObject( hdc, hpen );\r
3135     hbrush = CreateBrushIndirect( &stLB );\r
3136     holdbrush = SelectObject( hdc, hbrush );\r
3137 \r
3138     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3139 \r
3140     SelectObject( hdc, holdpen );\r
3141     SelectObject( hdc, holdbrush );\r
3142     DeleteObject( hpen );\r
3143     DeleteObject( hbrush );\r
3144 }\r
3145 \r
3146 BOOL HasHighlightInfo()\r
3147 {\r
3148     BOOL result = FALSE;\r
3149 \r
3150     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3151         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3152     {\r
3153         result = TRUE;\r
3154     }\r
3155 \r
3156     return result;\r
3157 }\r
3158 \r
3159 BOOL IsDrawArrowEnabled()\r
3160 {\r
3161     BOOL result = FALSE;\r
3162 \r
3163     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3164         result = TRUE;\r
3165     }\r
3166 \r
3167     return result;\r
3168 }\r
3169 \r
3170 VOID DrawArrowHighlight( HDC hdc )\r
3171 {\r
3172     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3173         DrawArrowBetweenSquares( hdc,\r
3174             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3175             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3176     }\r
3177 }\r
3178 \r
3179 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3180 {\r
3181     HRGN result = NULL;\r
3182 \r
3183     if( HasHighlightInfo() ) {\r
3184         int x1, y1, x2, y2;\r
3185         int sx, sy, dx, dy;\r
3186 \r
3187         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3188         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3189 \r
3190         sx = MIN( x1, x2 );\r
3191         sy = MIN( y1, y2 );\r
3192         dx = MAX( x1, x2 ) + squareSize;\r
3193         dy = MAX( y1, y2 ) + squareSize;\r
3194 \r
3195         result = CreateRectRgn( sx, sy, dx, dy );\r
3196     }\r
3197 \r
3198     return result;\r
3199 }\r
3200 \r
3201 /*\r
3202     Warning: this function modifies the behavior of several other functions. \r
3203     \r
3204     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3205     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3206     repaint is scattered all over the place, which is not good for features such as\r
3207     "arrow highlighting" that require a full repaint of the board.\r
3208 \r
3209     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3210     user interaction, when speed is not so important) but especially to avoid errors\r
3211     in the displayed graphics.\r
3212 \r
3213     In such patched places, I always try refer to this function so there is a single\r
3214     place to maintain knowledge.\r
3215     \r
3216     To restore the original behavior, just return FALSE unconditionally.\r
3217 */\r
3218 BOOL IsFullRepaintPreferrable()\r
3219 {\r
3220     BOOL result = FALSE;\r
3221 \r
3222     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3223         /* Arrow may appear on the board */\r
3224         result = TRUE;\r
3225     }\r
3226 \r
3227     return result;\r
3228 }\r
3229 \r
3230 /* \r
3231     This function is called by DrawPosition to know whether a full repaint must\r
3232     be forced or not.\r
3233 \r
3234     Only DrawPosition may directly call this function, which makes use of \r
3235     some state information. Other function should call DrawPosition specifying \r
3236     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3237 */\r
3238 BOOL DrawPositionNeedsFullRepaint()\r
3239 {\r
3240     BOOL result = FALSE;\r
3241 \r
3242     /* \r
3243         Probably a slightly better policy would be to trigger a full repaint\r
3244         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3245         but animation is fast enough that it's difficult to notice.\r
3246     */\r
3247     if( animInfo.piece == EmptySquare ) {\r
3248         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3249             result = TRUE;\r
3250         }\r
3251     }\r
3252 \r
3253     return result;\r
3254 }\r
3255 \r
3256 VOID\r
3257 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3258 {\r
3259   int row, column, x, y, square_color, piece_color;\r
3260   ChessSquare piece;\r
3261   HBRUSH oldBrush;\r
3262   HDC texture_hdc = NULL;\r
3263 \r
3264   /* [AS] Initialize background textures if needed */\r
3265   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3266       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3267       if( backTextureSquareSize != squareSize \r
3268        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3269           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3270           backTextureSquareSize = squareSize;\r
3271           RebuildTextureSquareInfo();\r
3272       }\r
3273 \r
3274       texture_hdc = CreateCompatibleDC( hdc );\r
3275   }\r
3276 \r
3277   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3278     for (column = 0; column < BOARD_WIDTH; column++) {\r
3279   \r
3280       SquareToPos(row, column, &x, &y);\r
3281 \r
3282       piece = board[row][column];\r
3283 \r
3284       square_color = ((column + row) % 2) == 1;\r
3285       if( gameInfo.variant == VariantXiangqi ) {\r
3286           square_color = !InPalace(row, column);\r
3287           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3288           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3289       }\r
3290       piece_color = (int) piece < (int) BlackPawn;\r
3291 \r
3292 \r
3293       /* [HGM] holdings file: light square or black */\r
3294       if(column == BOARD_LEFT-2) {\r
3295             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3296                 square_color = 1;\r
3297             else {\r
3298                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3299                 continue;\r
3300             }\r
3301       } else\r
3302       if(column == BOARD_RGHT + 1 ) {\r
3303             if( row < gameInfo.holdingsSize )\r
3304                 square_color = 1;\r
3305             else {\r
3306                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3307                 continue;\r
3308             }\r
3309       }\r
3310       if(column == BOARD_LEFT-1 ) /* left align */\r
3311             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3312       else if( column == BOARD_RGHT) /* right align */\r
3313             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3314       else\r
3315       if (appData.monoMode) {\r
3316         if (piece == EmptySquare) {\r
3317           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3318                  square_color ? WHITENESS : BLACKNESS);\r
3319         } else {\r
3320           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3321         }\r
3322       } \r
3323       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3324           /* [AS] Draw the square using a texture bitmap */\r
3325           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3326           int r = row, c = column; // [HGM] do not flip board in flipView\r
3327           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3328 \r
3329           DrawTile( x, y, \r
3330               squareSize, squareSize, \r
3331               hdc, \r
3332               texture_hdc,\r
3333               backTextureSquareInfo[r][c].mode,\r
3334               backTextureSquareInfo[r][c].x,\r
3335               backTextureSquareInfo[r][c].y );\r
3336 \r
3337           SelectObject( texture_hdc, hbm );\r
3338 \r
3339           if (piece != EmptySquare) {\r
3340               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3341           }\r
3342       }\r
3343       else {\r
3344         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3345 \r
3346         oldBrush = SelectObject(hdc, brush );\r
3347         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3348         SelectObject(hdc, oldBrush);\r
3349         if (piece != EmptySquare)\r
3350           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3351       }\r
3352     }\r
3353   }\r
3354 \r
3355   if( texture_hdc != NULL ) {\r
3356     DeleteDC( texture_hdc );\r
3357   }\r
3358 }\r
3359 \r
3360 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3361 void fputDW(FILE *f, int x)\r
3362 {\r
3363         fputc(x     & 255, f);\r
3364         fputc(x>>8  & 255, f);\r
3365         fputc(x>>16 & 255, f);\r
3366         fputc(x>>24 & 255, f);\r
3367 }\r
3368 \r
3369 #define MAX_CLIPS 200   /* more than enough */\r
3370 \r
3371 VOID\r
3372 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3373 {\r
3374 //  HBITMAP bufferBitmap;\r
3375   BITMAP bi;\r
3376 //  RECT Rect;\r
3377   HDC tmphdc;\r
3378   HBITMAP hbm;\r
3379   int w = 100, h = 50;\r
3380 \r
3381   if(logo == NULL) return;\r
3382 //  GetClientRect(hwndMain, &Rect);\r
3383 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3384 //                                      Rect.bottom-Rect.top+1);\r
3385   tmphdc = CreateCompatibleDC(hdc);\r
3386   hbm = SelectObject(tmphdc, logo);\r
3387   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3388             w = bi.bmWidth;\r
3389             h = bi.bmHeight;\r
3390   }\r
3391   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3392                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3393   SelectObject(tmphdc, hbm);\r
3394   DeleteDC(tmphdc);\r
3395 }\r
3396 \r
3397 static HDC hdcSeek;\r
3398 \r
3399 // [HGM] seekgraph\r
3400 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3401 {\r
3402     POINT stPt;\r
3403     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3404     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3405     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3406     SelectObject( hdcSeek, hp );\r
3407 }\r
3408 \r
3409 // front-end wrapper for drawing functions to do rectangles\r
3410 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3411 {\r
3412     HPEN hp;\r
3413     RECT rc;\r
3414 \r
3415     if (hdcSeek == NULL) {\r
3416     hdcSeek = GetDC(hwndMain);\r
3417       if (!appData.monoMode) {\r
3418         SelectPalette(hdcSeek, hPal, FALSE);\r
3419         RealizePalette(hdcSeek);\r
3420       }\r
3421     }\r
3422     hp = SelectObject( hdcSeek, gridPen );\r
3423     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3424     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3425     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3426     SelectObject( hdcSeek, hp );\r
3427 }\r
3428 \r
3429 // front-end wrapper for putting text in graph\r
3430 void DrawSeekText(char *buf, int x, int y)\r
3431 {\r
3432         SIZE stSize;\r
3433         SetBkMode( hdcSeek, TRANSPARENT );\r
3434         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3435         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3436 }\r
3437 \r
3438 void DrawSeekDot(int x, int y, int color)\r
3439 {\r
3440         int square = color & 0x80;\r
3441         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3442                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3443         color &= 0x7F;\r
3444         if(square)\r
3445             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3446                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3447         else\r
3448             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3449                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3450             SelectObject(hdcSeek, oldBrush);\r
3451 }\r
3452 \r
3453 VOID\r
3454 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3455 {\r
3456   static Board lastReq[2], lastDrawn[2];\r
3457   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3458   static int lastDrawnFlipView = 0;\r
3459   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3460   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3461   HDC tmphdc;\r
3462   HDC hdcmem;\r
3463   HBITMAP bufferBitmap;\r
3464   HBITMAP oldBitmap;\r
3465   RECT Rect;\r
3466   HRGN clips[MAX_CLIPS];\r
3467   ChessSquare dragged_piece = EmptySquare;\r
3468   int nr = twoBoards*partnerUp;\r
3469 \r
3470   /* I'm undecided on this - this function figures out whether a full\r
3471    * repaint is necessary on its own, so there's no real reason to have the\r
3472    * caller tell it that.  I think this can safely be set to FALSE - but\r
3473    * if we trust the callers not to request full repaints unnessesarily, then\r
3474    * we could skip some clipping work.  In other words, only request a full\r
3475    * redraw when the majority of pieces have changed positions (ie. flip, \r
3476    * gamestart and similar)  --Hawk\r
3477    */\r
3478   Boolean fullrepaint = repaint;\r
3479 \r
3480   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3481 \r
3482   if( DrawPositionNeedsFullRepaint() ) {\r
3483       fullrepaint = TRUE;\r
3484   }\r
3485 \r
3486   if (board == NULL) {\r
3487     if (!lastReqValid[nr]) {\r
3488       return;\r
3489     }\r
3490     board = lastReq[nr];\r
3491   } else {\r
3492     CopyBoard(lastReq[nr], board);\r
3493     lastReqValid[nr] = 1;\r
3494   }\r
3495 \r
3496   if (doingSizing) {\r
3497     return;\r
3498   }\r
3499 \r
3500   if (IsIconic(hwndMain)) {\r
3501     return;\r
3502   }\r
3503 \r
3504   if (hdc == NULL) {\r
3505     hdc = GetDC(hwndMain);\r
3506     if (!appData.monoMode) {\r
3507       SelectPalette(hdc, hPal, FALSE);\r
3508       RealizePalette(hdc);\r
3509     }\r
3510     releaseDC = TRUE;\r
3511   } else {\r
3512     releaseDC = FALSE;\r
3513   }\r
3514 \r
3515   /* Create some work-DCs */\r
3516   hdcmem = CreateCompatibleDC(hdc);\r
3517   tmphdc = CreateCompatibleDC(hdc);\r
3518 \r
3519   /* If dragging is in progress, we temporarely remove the piece */\r
3520   /* [HGM] or temporarily decrease count if stacked              */\r
3521   /*       !! Moved to before board compare !!                   */\r
3522   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3523     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3524     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3525             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3526         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3527     } else \r
3528     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3529             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3530         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3531     } else \r
3532         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3533   }\r
3534 \r
3535   /* Figure out which squares need updating by comparing the \r
3536    * newest board with the last drawn board and checking if\r
3537    * flipping has changed.\r
3538    */\r
3539   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3540     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3541       for (column = 0; column < BOARD_WIDTH; column++) {\r
3542         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3543           SquareToPos(row, column, &x, &y);\r
3544           clips[num_clips++] =\r
3545             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3546         }\r
3547       }\r
3548     }\r
3549    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3550     for (i=0; i<2; i++) {\r
3551       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3552           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3553         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3554             lastDrawnHighlight.sq[i].y >= 0) {\r
3555           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3556                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3557           clips[num_clips++] =\r
3558             CreateRectRgn(x - lineGap, y - lineGap, \r
3559                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3560         }\r
3561         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3562           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3563           clips[num_clips++] =\r
3564             CreateRectRgn(x - lineGap, y - lineGap, \r
3565                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3566         }\r
3567       }\r
3568     }\r
3569     for (i=0; i<2; i++) {\r
3570       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3571           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3572         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3573             lastDrawnPremove.sq[i].y >= 0) {\r
3574           SquareToPos(lastDrawnPremove.sq[i].y,\r
3575                       lastDrawnPremove.sq[i].x, &x, &y);\r
3576           clips[num_clips++] =\r
3577             CreateRectRgn(x - lineGap, y - lineGap, \r
3578                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3579         }\r
3580         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3581             premoveHighlightInfo.sq[i].y >= 0) {\r
3582           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3583                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3584           clips[num_clips++] =\r
3585             CreateRectRgn(x - lineGap, y - lineGap, \r
3586                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3587         }\r
3588       }\r
3589     }\r
3590    } else { // nr == 1\r
3591         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3592         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3593         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3594         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3595       for (i=0; i<2; i++) {\r
3596         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3597             partnerHighlightInfo.sq[i].y >= 0) {\r
3598           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3599                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3600           clips[num_clips++] =\r
3601             CreateRectRgn(x - lineGap, y - lineGap, \r
3602                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3603         }\r
3604         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3605             oldPartnerHighlight.sq[i].y >= 0) {\r
3606           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3607                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3608           clips[num_clips++] =\r
3609             CreateRectRgn(x - lineGap, y - lineGap, \r
3610                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3611         }\r
3612       }\r
3613    }\r
3614   } else {\r
3615     fullrepaint = TRUE;\r
3616   }\r
3617 \r
3618   /* Create a buffer bitmap - this is the actual bitmap\r
3619    * being written to.  When all the work is done, we can\r
3620    * copy it to the real DC (the screen).  This avoids\r
3621    * the problems with flickering.\r
3622    */\r
3623   GetClientRect(hwndMain, &Rect);\r
3624   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3625                                         Rect.bottom-Rect.top+1);\r
3626   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3627   if (!appData.monoMode) {\r
3628     SelectPalette(hdcmem, hPal, FALSE);\r
3629   }\r
3630 \r
3631   /* Create clips for dragging */\r
3632   if (!fullrepaint) {\r
3633     if (dragInfo.from.x >= 0) {\r
3634       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3635       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3636     }\r
3637     if (dragInfo.start.x >= 0) {\r
3638       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3639       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3640     }\r
3641     if (dragInfo.pos.x >= 0) {\r
3642       x = dragInfo.pos.x - squareSize / 2;\r
3643       y = dragInfo.pos.y - squareSize / 2;\r
3644       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3645     }\r
3646     if (dragInfo.lastpos.x >= 0) {\r
3647       x = dragInfo.lastpos.x - squareSize / 2;\r
3648       y = dragInfo.lastpos.y - squareSize / 2;\r
3649       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3650     }\r
3651   }\r
3652 \r
3653   /* Are we animating a move?  \r
3654    * If so, \r
3655    *   - remove the piece from the board (temporarely)\r
3656    *   - calculate the clipping region\r
3657    */\r
3658   if (!fullrepaint) {\r
3659     if (animInfo.piece != EmptySquare) {\r
3660       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3661       x = boardRect.left + animInfo.lastpos.x;\r
3662       y = boardRect.top + animInfo.lastpos.y;\r
3663       x2 = boardRect.left + animInfo.pos.x;\r
3664       y2 = boardRect.top + animInfo.pos.y;\r
3665       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3666       /* Slight kludge.  The real problem is that after AnimateMove is\r
3667          done, the position on the screen does not match lastDrawn.\r
3668          This currently causes trouble only on e.p. captures in\r
3669          atomic, where the piece moves to an empty square and then\r
3670          explodes.  The old and new positions both had an empty square\r
3671          at the destination, but animation has drawn a piece there and\r
3672          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3673       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3674     }\r
3675   }\r
3676 \r
3677   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3678   if (num_clips == 0)\r
3679     fullrepaint = TRUE;\r
3680 \r
3681   /* Set clipping on the memory DC */\r
3682   if (!fullrepaint) {\r
3683     SelectClipRgn(hdcmem, clips[0]);\r
3684     for (x = 1; x < num_clips; x++) {\r
3685       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3686         abort();  // this should never ever happen!\r
3687     }\r
3688   }\r
3689 \r
3690   /* Do all the drawing to the memory DC */\r
3691   if(explodeInfo.radius) { // [HGM] atomic\r
3692         HBRUSH oldBrush;\r
3693         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3694         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3695         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3696         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3697         x += squareSize/2;\r
3698         y += squareSize/2;\r
3699         if(!fullrepaint) {\r
3700           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3701           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3702         }\r
3703         DrawGridOnDC(hdcmem);\r
3704         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3705         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3706         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3707         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3708         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3709         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3710         SelectObject(hdcmem, oldBrush);\r
3711   } else {\r
3712     DrawGridOnDC(hdcmem);\r
3713     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3714         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3715         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3716     } else {\r
3717         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3718         oldPartnerHighlight = partnerHighlightInfo;\r
3719     }\r
3720     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3721   }\r
3722   if(nr == 0) // [HGM] dual: markers only on left board\r
3723   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3724     for (column = 0; column < BOARD_WIDTH; column++) {\r
3725         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3726             HBRUSH oldBrush = SelectObject(hdcmem, \r
3727                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3728             SquareToPos(row, column, &x, &y);\r
3729             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3730                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3731             SelectObject(hdcmem, oldBrush);\r
3732         }\r
3733     }\r
3734   }\r
3735   if(logoHeight) {\r
3736         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3737         if(appData.autoLogo) {\r
3738           \r
3739           switch(gameMode) { // pick logos based on game mode\r
3740             case IcsObserving:\r
3741                 whiteLogo = second.programLogo; // ICS logo\r
3742                 blackLogo = second.programLogo;\r
3743             default:\r
3744                 break;\r
3745             case IcsPlayingWhite:\r
3746                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3747                 blackLogo = second.programLogo; // ICS logo\r
3748                 break;\r
3749             case IcsPlayingBlack:\r
3750                 whiteLogo = second.programLogo; // ICS logo\r
3751                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3752                 break;\r
3753             case TwoMachinesPlay:\r
3754                 if(first.twoMachinesColor[0] == 'b') {\r
3755                     whiteLogo = second.programLogo;\r
3756                     blackLogo = first.programLogo;\r
3757                 }\r
3758                 break;\r
3759             case MachinePlaysWhite:\r
3760                 blackLogo = userLogo;\r
3761                 break;\r
3762             case MachinePlaysBlack:\r
3763                 whiteLogo = userLogo;\r
3764                 blackLogo = first.programLogo;\r
3765           }\r
3766         }\r
3767         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3768         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3769   }\r
3770 \r
3771   if( appData.highlightMoveWithArrow ) {\r
3772     DrawArrowHighlight(hdcmem);\r
3773   }\r
3774 \r
3775   DrawCoordsOnDC(hdcmem);\r
3776 \r
3777   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3778                  /* to make sure lastDrawn contains what is actually drawn */\r
3779 \r
3780   /* Put the dragged piece back into place and draw it (out of place!) */\r
3781     if (dragged_piece != EmptySquare) {\r
3782     /* [HGM] or restack */\r
3783     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3784                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3785     else\r
3786     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3787                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3788     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3789     x = dragInfo.pos.x - squareSize / 2;\r
3790     y = dragInfo.pos.y - squareSize / 2;\r
3791     DrawPieceOnDC(hdcmem, dragged_piece,\r
3792                   ((int) dragged_piece < (int) BlackPawn), \r
3793                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3794   }   \r
3795   \r
3796   /* Put the animated piece back into place and draw it */\r
3797   if (animInfo.piece != EmptySquare) {\r
3798     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3799     x = boardRect.left + animInfo.pos.x;\r
3800     y = boardRect.top + animInfo.pos.y;\r
3801     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3802                   ((int) animInfo.piece < (int) BlackPawn),\r
3803                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3804   }\r
3805 \r
3806   /* Release the bufferBitmap by selecting in the old bitmap \r
3807    * and delete the memory DC\r
3808    */\r
3809   SelectObject(hdcmem, oldBitmap);\r
3810   DeleteDC(hdcmem);\r
3811 \r
3812   /* Set clipping on the target DC */\r
3813   if (!fullrepaint) {\r
3814     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3815         RECT rect;\r
3816         GetRgnBox(clips[x], &rect);\r
3817         DeleteObject(clips[x]);\r
3818         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3819                           rect.right + wpMain.width/2, rect.bottom);\r
3820     }\r
3821     SelectClipRgn(hdc, clips[0]);\r
3822     for (x = 1; x < num_clips; x++) {\r
3823       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3824         abort();   // this should never ever happen!\r
3825     } \r
3826   }\r
3827 \r
3828   /* Copy the new bitmap onto the screen in one go.\r
3829    * This way we avoid any flickering\r
3830    */\r
3831   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3832   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3833          boardRect.right - boardRect.left,\r
3834          boardRect.bottom - boardRect.top,\r
3835          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3836   if(saveDiagFlag) { \r
3837     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3838     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3839 \r
3840     GetObject(bufferBitmap, sizeof(b), &b);\r
3841     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3842         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3843         bih.biWidth = b.bmWidth;\r
3844         bih.biHeight = b.bmHeight;\r
3845         bih.biPlanes = 1;\r
3846         bih.biBitCount = b.bmBitsPixel;\r
3847         bih.biCompression = 0;\r
3848         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3849         bih.biXPelsPerMeter = 0;\r
3850         bih.biYPelsPerMeter = 0;\r
3851         bih.biClrUsed = 0;\r
3852         bih.biClrImportant = 0;\r
3853 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3854 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3855         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3856 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3857 \r
3858         wb = b.bmWidthBytes;\r
3859         // count colors\r
3860         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3861                 int k = ((int*) pData)[i];\r
3862                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3863                 if(j >= 16) break;\r
3864                 color[j] = k;\r
3865                 if(j >= nrColors) nrColors = j+1;\r
3866         }\r
3867         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3868                 INT p = 0;\r
3869                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3870                     for(w=0; w<(wb>>2); w+=2) {\r
3871                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3872                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3873                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3874                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3875                         pData[p++] = m | j<<4;\r
3876                     }\r
3877                     while(p&3) pData[p++] = 0;\r
3878                 }\r
3879                 fac = 3;\r
3880                 wb = ((wb+31)>>5)<<2;\r
3881         }\r
3882         // write BITMAPFILEHEADER\r
3883         fprintf(diagFile, "BM");\r
3884         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3885         fputDW(diagFile, 0);\r
3886         fputDW(diagFile, 0x36 + (fac?64:0));\r
3887         // write BITMAPINFOHEADER\r
3888         fputDW(diagFile, 40);\r
3889         fputDW(diagFile, b.bmWidth);\r
3890         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3891         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3892         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3893         fputDW(diagFile, 0);\r
3894         fputDW(diagFile, 0);\r
3895         fputDW(diagFile, 0);\r
3896         fputDW(diagFile, 0);\r
3897         fputDW(diagFile, 0);\r
3898         fputDW(diagFile, 0);\r
3899         // write color table\r
3900         if(fac)\r
3901         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3902         // write bitmap data\r
3903         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3904                 fputc(pData[i], diagFile);\r
3905      }\r
3906   }\r
3907 \r
3908   SelectObject(tmphdc, oldBitmap);\r
3909 \r
3910   /* Massive cleanup */\r
3911   for (x = 0; x < num_clips; x++)\r
3912     DeleteObject(clips[x]);\r
3913 \r
3914   DeleteDC(tmphdc);\r
3915   DeleteObject(bufferBitmap);\r
3916 \r
3917   if (releaseDC) \r
3918     ReleaseDC(hwndMain, hdc);\r
3919   \r
3920   if (lastDrawnFlipView != flipView && nr == 0) {\r
3921     if (flipView)\r
3922       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3923     else\r
3924       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3925   }\r
3926 \r
3927 /*  CopyBoard(lastDrawn, board);*/\r
3928   lastDrawnHighlight = highlightInfo;\r
3929   lastDrawnPremove   = premoveHighlightInfo;\r
3930   lastDrawnFlipView = flipView;\r
3931   lastDrawnValid[nr] = 1;\r
3932 }\r
3933 \r
3934 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3935 int\r
3936 SaveDiagram(f)\r
3937      FILE *f;\r
3938 {\r
3939     saveDiagFlag = 1; diagFile = f;\r
3940     HDCDrawPosition(NULL, TRUE, NULL);\r
3941 \r
3942     saveDiagFlag = 0;\r
3943 \r
3944 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3945     \r
3946     fclose(f);\r
3947     return TRUE;\r
3948 }\r
3949 \r
3950 \r
3951 /*---------------------------------------------------------------------------*\\r
3952 | CLIENT PAINT PROCEDURE\r
3953 |   This is the main event-handler for the WM_PAINT message.\r
3954 |\r
3955 \*---------------------------------------------------------------------------*/\r
3956 VOID\r
3957 PaintProc(HWND hwnd)\r
3958 {\r
3959   HDC         hdc;\r
3960   PAINTSTRUCT ps;\r
3961   HFONT       oldFont;\r
3962 \r
3963   if((hdc = BeginPaint(hwnd, &ps))) {\r
3964     if (IsIconic(hwnd)) {\r
3965       DrawIcon(hdc, 2, 2, iconCurrent);\r
3966     } else {\r
3967       if (!appData.monoMode) {\r
3968         SelectPalette(hdc, hPal, FALSE);\r
3969         RealizePalette(hdc);\r
3970       }\r
3971       HDCDrawPosition(hdc, 1, NULL);\r
3972       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3973         flipView = !flipView; partnerUp = !partnerUp;\r
3974         HDCDrawPosition(hdc, 1, NULL);\r
3975         flipView = !flipView; partnerUp = !partnerUp;\r
3976       }\r
3977       oldFont =\r
3978         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3979       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3980                  ETO_CLIPPED|ETO_OPAQUE,\r
3981                  &messageRect, messageText, strlen(messageText), NULL);\r
3982       SelectObject(hdc, oldFont);\r
3983       DisplayBothClocks();\r
3984     }\r
3985     EndPaint(hwnd,&ps);\r
3986   }\r
3987 \r
3988   return;\r
3989 }\r
3990 \r
3991 \r
3992 /*\r
3993  * If the user selects on a border boundary, return -1; if off the board,\r
3994  *   return -2.  Otherwise map the event coordinate to the square.\r
3995  * The offset boardRect.left or boardRect.top must already have been\r
3996  *   subtracted from x.\r
3997  */\r
3998 int EventToSquare(x, limit)\r
3999      int x, limit;\r
4000 {\r
4001   if (x <= 0)\r
4002     return -2;\r
4003   if (x < lineGap)\r
4004     return -1;\r
4005   x -= lineGap;\r
4006   if ((x % (squareSize + lineGap)) >= squareSize)\r
4007     return -1;\r
4008   x /= (squareSize + lineGap);\r
4009     if (x >= limit)\r
4010     return -2;\r
4011   return x;\r
4012 }\r
4013 \r
4014 typedef struct {\r
4015   char piece;\r
4016   int command;\r
4017   char* name;\r
4018 } DropEnable;\r
4019 \r
4020 DropEnable dropEnables[] = {\r
4021   { 'P', DP_Pawn, N_("Pawn") },\r
4022   { 'N', DP_Knight, N_("Knight") },\r
4023   { 'B', DP_Bishop, N_("Bishop") },\r
4024   { 'R', DP_Rook, N_("Rook") },\r
4025   { 'Q', DP_Queen, N_("Queen") },\r
4026 };\r
4027 \r
4028 VOID\r
4029 SetupDropMenu(HMENU hmenu)\r
4030 {\r
4031   int i, count, enable;\r
4032   char *p;\r
4033   extern char white_holding[], black_holding[];\r
4034   char item[MSG_SIZ];\r
4035 \r
4036   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4037     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4038                dropEnables[i].piece);\r
4039     count = 0;\r
4040     while (p && *p++ == dropEnables[i].piece) count++;\r
4041       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4042     enable = count > 0 || !appData.testLegality\r
4043       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4044                       && !appData.icsActive);\r
4045     ModifyMenu(hmenu, dropEnables[i].command,\r
4046                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4047                dropEnables[i].command, item);\r
4048   }\r
4049 }\r
4050 \r
4051 void DragPieceBegin(int x, int y)\r
4052 {\r
4053       dragInfo.lastpos.x = boardRect.left + x;\r
4054       dragInfo.lastpos.y = boardRect.top + y;\r
4055       dragInfo.from.x = fromX;\r
4056       dragInfo.from.y = fromY;\r
4057       dragInfo.start = dragInfo.from;\r
4058       SetCapture(hwndMain);\r
4059 }\r
4060 \r
4061 void DragPieceEnd(int x, int y)\r
4062 {\r
4063     ReleaseCapture();\r
4064     dragInfo.start.x = dragInfo.start.y = -1;\r
4065     dragInfo.from = dragInfo.start;\r
4066     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4067 }\r
4068 \r
4069 /* Event handler for mouse messages */\r
4070 VOID\r
4071 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4072 {\r
4073   int x, y, menuNr;\r
4074   POINT pt;\r
4075   static int recursive = 0;\r
4076   HMENU hmenu;\r
4077   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4078 \r
4079   if (recursive) {\r
4080     if (message == WM_MBUTTONUP) {\r
4081       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4082          to the middle button: we simulate pressing the left button too!\r
4083          */\r
4084       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4085       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4086     }\r
4087     return;\r
4088   }\r
4089   recursive++;\r
4090   \r
4091   pt.x = LOWORD(lParam);\r
4092   pt.y = HIWORD(lParam);\r
4093   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4094   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4095   if (!flipView && y >= 0) {\r
4096     y = BOARD_HEIGHT - 1 - y;\r
4097   }\r
4098   if (flipView && x >= 0) {\r
4099     x = BOARD_WIDTH - 1 - x;\r
4100   }\r
4101 \r
4102   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4103 \r
4104   switch (message) {\r
4105   case WM_LBUTTONDOWN:\r
4106       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4107         if (gameMode == EditPosition) {\r
4108           SetWhiteToPlayEvent();\r
4109         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4110           AdjustClock(flipClock, -1);\r
4111         } else if (gameMode == IcsPlayingBlack ||\r
4112                    gameMode == MachinePlaysWhite) {\r
4113           CallFlagEvent();\r
4114         }\r
4115       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4116         if (gameMode == EditPosition) {\r
4117           SetBlackToPlayEvent();\r
4118         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4119           AdjustClock(!flipClock, -1);\r
4120         } else if (gameMode == IcsPlayingWhite ||\r
4121                    gameMode == MachinePlaysBlack) {\r
4122           CallFlagEvent();\r
4123         }\r
4124       }\r
4125       dragInfo.start.x = dragInfo.start.y = -1;\r
4126       dragInfo.from = dragInfo.start;\r
4127     if(fromX == -1 && frozen) { // not sure where this is for\r
4128                 fromX = fromY = -1; \r
4129       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4130       break;\r
4131     }\r
4132       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4133       DrawPosition(TRUE, NULL);\r
4134     break;\r
4135 \r
4136   case WM_LBUTTONUP:\r
4137       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4138       DrawPosition(TRUE, NULL);\r
4139     break;\r
4140 \r
4141   case WM_MOUSEMOVE:\r
4142     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4143     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4144     if ((appData.animateDragging || appData.highlightDragging)\r
4145         && (wParam & MK_LBUTTON)\r
4146         && dragInfo.from.x >= 0) \r
4147     {\r
4148       BOOL full_repaint = FALSE;\r
4149 \r
4150       if (appData.animateDragging) {\r
4151         dragInfo.pos = pt;\r
4152       }\r
4153       if (appData.highlightDragging) {\r
4154         SetHighlights(fromX, fromY, x, y);\r
4155         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4156             full_repaint = TRUE;\r
4157         }\r
4158       }\r
4159       \r
4160       DrawPosition( full_repaint, NULL);\r
4161       \r
4162       dragInfo.lastpos = dragInfo.pos;\r
4163     }\r
4164     break;\r
4165 \r
4166   case WM_MOUSEWHEEL: // [DM]\r
4167     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4168        /* Mouse Wheel is being rolled forward\r
4169         * Play moves forward\r
4170         */\r
4171        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4172                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4173        /* Mouse Wheel is being rolled backward\r
4174         * Play moves backward\r
4175         */\r
4176        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4177                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4178     }\r
4179     break;\r
4180 \r
4181   case WM_MBUTTONUP:\r
4182   case WM_RBUTTONUP:\r
4183     ReleaseCapture();\r
4184     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4185     break;\r
4186  \r
4187   case WM_MBUTTONDOWN:\r
4188   case WM_RBUTTONDOWN:\r
4189     ErrorPopDown();\r
4190     ReleaseCapture();\r
4191     fromX = fromY = -1;\r
4192     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4193     dragInfo.start.x = dragInfo.start.y = -1;\r
4194     dragInfo.from = dragInfo.start;\r
4195     dragInfo.lastpos = dragInfo.pos;\r
4196     if (appData.highlightDragging) {\r
4197       ClearHighlights();\r
4198     }\r
4199     if(y == -2) {\r
4200       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4201       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4202           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4203       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4204           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4205       }\r
4206       break;\r
4207     }\r
4208     DrawPosition(TRUE, NULL);\r
4209 \r
4210     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4211     switch (menuNr) {\r
4212     case 0:\r
4213       if (message == WM_MBUTTONDOWN) {\r
4214         buttonCount = 3;  /* even if system didn't think so */\r
4215         if (wParam & MK_SHIFT) \r
4216           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4217         else\r
4218           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4219       } else { /* message == WM_RBUTTONDOWN */\r
4220         /* Just have one menu, on the right button.  Windows users don't\r
4221            think to try the middle one, and sometimes other software steals\r
4222            it, or it doesn't really exist. */\r
4223         if(gameInfo.variant != VariantShogi)\r
4224             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4225         else\r
4226             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4227       }\r
4228       break;\r
4229     case 2:\r
4230       SetCapture(hwndMain);
4231       break;\r
4232     case 1:\r
4233       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4234       SetupDropMenu(hmenu);\r
4235       MenuPopup(hwnd, pt, hmenu, -1);\r
4236     default:\r
4237       break;\r
4238     }\r
4239     break;\r
4240   }\r
4241 \r
4242   recursive--;\r
4243 }\r
4244 \r
4245 /* Preprocess messages for buttons in main window */\r
4246 LRESULT CALLBACK\r
4247 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4248 {\r
4249   int id = GetWindowLong(hwnd, GWL_ID);\r
4250   int i, dir;\r
4251 \r
4252   for (i=0; i<N_BUTTONS; i++) {\r
4253     if (buttonDesc[i].id == id) break;\r
4254   }\r
4255   if (i == N_BUTTONS) return 0;\r
4256   switch (message) {\r
4257   case WM_KEYDOWN:\r
4258     switch (wParam) {\r
4259     case VK_LEFT:\r
4260     case VK_RIGHT:\r
4261       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4262       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4263       return TRUE;\r
4264     }\r
4265     break;\r
4266   case WM_CHAR:\r
4267     switch (wParam) {\r
4268     case '\r':\r
4269       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4270       return TRUE;\r
4271     default:\r
4272       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4273         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4274         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4275         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4276         SetFocus(h);\r
4277         SendMessage(h, WM_CHAR, wParam, lParam);\r
4278         return TRUE;\r
4279       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4280         PopUpMoveDialog((char)wParam);\r
4281       }\r
4282       break;\r
4283     }\r
4284     break;\r
4285   }\r
4286   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4287 }\r
4288 \r
4289 /* Process messages for Promotion dialog box */\r
4290 LRESULT CALLBACK\r
4291 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4292 {\r
4293   char promoChar;\r
4294 \r
4295   switch (message) {\r
4296   case WM_INITDIALOG: /* message: initialize dialog box */\r
4297     /* Center the dialog over the application window */\r
4298     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4299     Translate(hDlg, DLG_PromotionKing);\r
4300     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4301       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4302        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4303        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4304                SW_SHOW : SW_HIDE);\r
4305     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4306     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4307        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4308          PieceToChar(WhiteAngel) != '~') ||\r
4309         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4310          PieceToChar(BlackAngel) != '~')   ) ?\r
4311                SW_SHOW : SW_HIDE);\r
4312     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4313        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4314          PieceToChar(WhiteMarshall) != '~') ||\r
4315         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4316          PieceToChar(BlackMarshall) != '~')   ) ?\r
4317                SW_SHOW : SW_HIDE);\r
4318     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4319     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4320        gameInfo.variant != VariantShogi ?\r
4321                SW_SHOW : SW_HIDE);\r
4322     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4323        gameInfo.variant != VariantShogi ?\r
4324                SW_SHOW : SW_HIDE);\r
4325     if(gameInfo.variant == VariantShogi) {\r
4326         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4327         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4328         SetWindowText(hDlg, "Promote?");\r
4329     }\r
4330     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4331        gameInfo.variant == VariantSuper ?\r
4332                SW_SHOW : SW_HIDE);\r
4333     return TRUE;\r
4334 \r
4335   case WM_COMMAND: /* message: received a command */\r
4336     switch (LOWORD(wParam)) {\r
4337     case IDCANCEL:\r
4338       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4339       ClearHighlights();\r
4340       DrawPosition(FALSE, NULL);\r
4341       return TRUE;\r
4342     case PB_King:\r
4343       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4344       break;\r
4345     case PB_Queen:\r
4346       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4347       break;\r
4348     case PB_Rook:\r
4349       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4350       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4351       break;\r
4352     case PB_Bishop:\r
4353       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4354       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4355       break;\r
4356     case PB_Chancellor:\r
4357       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4358       break;\r
4359     case PB_Archbishop:\r
4360       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4361       break;\r
4362     case PB_Knight:\r
4363       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4364       break;\r
4365     default:\r
4366       return FALSE;\r
4367     }\r
4368     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4369     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4370     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4371     fromX = fromY = -1;\r
4372     if (!appData.highlightLastMove) {\r
4373       ClearHighlights();\r
4374       DrawPosition(FALSE, NULL);\r
4375     }\r
4376     return TRUE;\r
4377   }\r
4378   return FALSE;\r
4379 }\r
4380 \r
4381 /* Pop up promotion dialog */\r
4382 VOID\r
4383 PromotionPopup(HWND hwnd)\r
4384 {\r
4385   FARPROC lpProc;\r
4386 \r
4387   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4388   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4389     hwnd, (DLGPROC)lpProc);\r
4390   FreeProcInstance(lpProc);\r
4391 }\r
4392 \r
4393 void\r
4394 PromotionPopUp()\r
4395 {\r
4396   DrawPosition(TRUE, NULL);\r
4397   PromotionPopup(hwndMain);\r
4398 }\r
4399 \r
4400 /* Toggle ShowThinking */\r
4401 VOID\r
4402 ToggleShowThinking()\r
4403 {\r
4404   appData.showThinking = !appData.showThinking;\r
4405   ShowThinkingEvent();\r
4406 }\r
4407 \r
4408 VOID\r
4409 LoadGameDialog(HWND hwnd, char* title)\r
4410 {\r
4411   UINT number = 0;\r
4412   FILE *f;\r
4413   char fileTitle[MSG_SIZ];\r
4414   f = OpenFileDialog(hwnd, "rb", "",\r
4415                      appData.oldSaveStyle ? "gam" : "pgn",\r
4416                      GAME_FILT,\r
4417                      title, &number, fileTitle, NULL);\r
4418   if (f != NULL) {\r
4419     cmailMsgLoaded = FALSE;\r
4420     if (number == 0) {\r
4421       int error = GameListBuild(f);\r
4422       if (error) {\r
4423         DisplayError(_("Cannot build game list"), error);\r
4424       } else if (!ListEmpty(&gameList) &&\r
4425                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4426         GameListPopUp(f, fileTitle);\r
4427         return;\r
4428       }\r
4429       GameListDestroy();\r
4430       number = 1;\r
4431     }\r
4432     LoadGame(f, number, fileTitle, FALSE);\r
4433   }\r
4434 }\r
4435 \r
4436 int get_term_width()\r
4437 {\r
4438     HDC hdc;\r
4439     TEXTMETRIC tm;\r
4440     RECT rc;\r
4441     HFONT hfont, hold_font;\r
4442     LOGFONT lf;\r
4443     HWND hText;\r
4444 \r
4445     if (hwndConsole)\r
4446         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4447     else\r
4448         return 79;\r
4449 \r
4450     // get the text metrics\r
4451     hdc = GetDC(hText);\r
4452     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4453     if (consoleCF.dwEffects & CFE_BOLD)\r
4454         lf.lfWeight = FW_BOLD;\r
4455     if (consoleCF.dwEffects & CFE_ITALIC)\r
4456         lf.lfItalic = TRUE;\r
4457     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4458         lf.lfStrikeOut = TRUE;\r
4459     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4460         lf.lfUnderline = TRUE;\r
4461     hfont = CreateFontIndirect(&lf);\r
4462     hold_font = SelectObject(hdc, hfont);\r
4463     GetTextMetrics(hdc, &tm);\r
4464     SelectObject(hdc, hold_font);\r
4465     DeleteObject(hfont);\r
4466     ReleaseDC(hText, hdc);\r
4467 \r
4468     // get the rectangle\r
4469     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4470 \r
4471     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4472 }\r
4473 \r
4474 void UpdateICSWidth(HWND hText)\r
4475 {\r
4476     LONG old_width, new_width;\r
4477 \r
4478     new_width = get_term_width(hText, FALSE);\r
4479     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4480     if (new_width != old_width)\r
4481     {\r
4482         ics_update_width(new_width);\r
4483         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4484     }\r
4485 }\r
4486 \r
4487 VOID\r
4488 ChangedConsoleFont()\r
4489 {\r
4490   CHARFORMAT cfmt;\r
4491   CHARRANGE tmpsel, sel;\r
4492   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4493   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4494   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4495   PARAFORMAT paraf;\r
4496 \r
4497   cfmt.cbSize = sizeof(CHARFORMAT);\r
4498   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4499     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4500                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4501   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4502    * size.  This was undocumented in the version of MSVC++ that I had\r
4503    * when I wrote the code, but is apparently documented now.\r
4504    */\r
4505   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4506   cfmt.bCharSet = f->lf.lfCharSet;\r
4507   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4508   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4509   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4510   /* Why are the following seemingly needed too? */\r
4511   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4512   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4513   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4514   tmpsel.cpMin = 0;\r
4515   tmpsel.cpMax = -1; /*999999?*/\r
4516   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4517   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4518   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4519    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4520    */\r
4521   paraf.cbSize = sizeof(paraf);\r
4522   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4523   paraf.dxStartIndent = 0;\r
4524   paraf.dxOffset = WRAP_INDENT;\r
4525   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4526   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4527   UpdateICSWidth(hText);\r
4528 }\r
4529 \r
4530 /*---------------------------------------------------------------------------*\\r
4531  *\r
4532  * Window Proc for main window\r
4533  *\r
4534 \*---------------------------------------------------------------------------*/\r
4535 \r
4536 /* Process messages for main window, etc. */\r
4537 LRESULT CALLBACK\r
4538 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4539 {\r
4540   FARPROC lpProc;\r
4541   int wmId, wmEvent;\r
4542   char *defName;\r
4543   FILE *f;\r
4544   UINT number;\r
4545   char fileTitle[MSG_SIZ];\r
4546   char buf[MSG_SIZ];\r
4547   static SnapData sd;\r
4548 \r
4549   switch (message) {\r
4550 \r
4551   case WM_PAINT: /* message: repaint portion of window */\r
4552     PaintProc(hwnd);\r
4553     break;\r
4554 \r
4555   case WM_ERASEBKGND:\r
4556     if (IsIconic(hwnd)) {\r
4557       /* Cheat; change the message */\r
4558       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4559     } else {\r
4560       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4561     }\r
4562     break;\r
4563 \r
4564   case WM_LBUTTONDOWN:\r
4565   case WM_MBUTTONDOWN:\r
4566   case WM_RBUTTONDOWN:\r
4567   case WM_LBUTTONUP:\r
4568   case WM_MBUTTONUP:\r
4569   case WM_RBUTTONUP:\r
4570   case WM_MOUSEMOVE:\r
4571   case WM_MOUSEWHEEL:\r
4572     MouseEvent(hwnd, message, wParam, lParam);\r
4573     break;\r
4574 \r
4575   JAWS_KB_NAVIGATION\r
4576 \r
4577   case WM_CHAR:\r
4578     \r
4579     JAWS_ALT_INTERCEPT\r
4580 \r
4581     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4582         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4583         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4584         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4585         SetFocus(h);\r
4586         SendMessage(h, message, wParam, lParam);\r
4587     } else if(lParam != KF_REPEAT) {\r
4588         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4589                 PopUpMoveDialog((char)wParam);\r
4590         } else if((char)wParam == 003) CopyGameToClipboard();\r
4591          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4592     }\r
4593 \r
4594     break;\r
4595 \r
4596   case WM_PALETTECHANGED:\r
4597     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4598       int nnew;\r
4599       HDC hdc = GetDC(hwndMain);\r
4600       SelectPalette(hdc, hPal, TRUE);\r
4601       nnew = RealizePalette(hdc);\r
4602       if (nnew > 0) {\r
4603         paletteChanged = TRUE;\r
4604         InvalidateRect(hwnd, &boardRect, FALSE);\r
4605       }\r
4606       ReleaseDC(hwnd, hdc);\r
4607     }\r
4608     break;\r
4609 \r
4610   case WM_QUERYNEWPALETTE:\r
4611     if (!appData.monoMode /*&& paletteChanged*/) {\r
4612       int nnew;\r
4613       HDC hdc = GetDC(hwndMain);\r
4614       paletteChanged = FALSE;\r
4615       SelectPalette(hdc, hPal, FALSE);\r
4616       nnew = RealizePalette(hdc);\r
4617       if (nnew > 0) {\r
4618         InvalidateRect(hwnd, &boardRect, FALSE);\r
4619       }\r
4620       ReleaseDC(hwnd, hdc);\r
4621       return TRUE;\r
4622     }\r
4623     return FALSE;\r
4624 \r
4625   case WM_COMMAND: /* message: command from application menu */\r
4626     wmId    = LOWORD(wParam);\r
4627     wmEvent = HIWORD(wParam);\r
4628 \r
4629     switch (wmId) {\r
4630     case IDM_NewGame:\r
4631       ResetGameEvent();\r
4632       SAY("new game enter a move to play against the computer with white");\r
4633       break;\r
4634 \r
4635     case IDM_NewGameFRC:\r
4636       if( NewGameFRC() == 0 ) {\r
4637         ResetGameEvent();\r
4638       }\r
4639       break;\r
4640 \r
4641     case IDM_NewVariant:\r
4642       NewVariantPopup(hwnd);\r
4643       break;\r
4644 \r
4645     case IDM_LoadGame:\r
4646       LoadGameDialog(hwnd, _("Load Game from File"));\r
4647       break;\r
4648 \r
4649     case IDM_LoadNextGame:\r
4650       ReloadGame(1);\r
4651       break;\r
4652 \r
4653     case IDM_LoadPrevGame:\r
4654       ReloadGame(-1);\r
4655       break;\r
4656 \r
4657     case IDM_ReloadGame:\r
4658       ReloadGame(0);\r
4659       break;\r
4660 \r
4661     case IDM_LoadPosition:\r
4662       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4663         Reset(FALSE, TRUE);\r
4664       }\r
4665       number = 1;\r
4666       f = OpenFileDialog(hwnd, "rb", "",\r
4667                          appData.oldSaveStyle ? "pos" : "fen",\r
4668                          POSITION_FILT,\r
4669                          _("Load Position from File"), &number, fileTitle, NULL);\r
4670       if (f != NULL) {\r
4671         LoadPosition(f, number, fileTitle);\r
4672       }\r
4673       break;\r
4674 \r
4675     case IDM_LoadNextPosition:\r
4676       ReloadPosition(1);\r
4677       break;\r
4678 \r
4679     case IDM_LoadPrevPosition:\r
4680       ReloadPosition(-1);\r
4681       break;\r
4682 \r
4683     case IDM_ReloadPosition:\r
4684       ReloadPosition(0);\r
4685       break;\r
4686 \r
4687     case IDM_SaveGame:\r
4688       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4689       f = OpenFileDialog(hwnd, "a", defName,\r
4690                          appData.oldSaveStyle ? "gam" : "pgn",\r
4691                          GAME_FILT,\r
4692                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4693       if (f != NULL) {\r
4694         SaveGame(f, 0, "");\r
4695       }\r
4696       break;\r
4697 \r
4698     case IDM_SavePosition:\r
4699       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4700       f = OpenFileDialog(hwnd, "a", defName,\r
4701                          appData.oldSaveStyle ? "pos" : "fen",\r
4702                          POSITION_FILT,\r
4703                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4704       if (f != NULL) {\r
4705         SavePosition(f, 0, "");\r
4706       }\r
4707       break;\r
4708 \r
4709     case IDM_SaveDiagram:\r
4710       defName = "diagram";\r
4711       f = OpenFileDialog(hwnd, "wb", defName,\r
4712                          "bmp",\r
4713                          DIAGRAM_FILT,\r
4714                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4715       if (f != NULL) {\r
4716         SaveDiagram(f);\r
4717       }\r
4718       break;\r
4719 \r
4720     case IDM_CopyGame:\r
4721       CopyGameToClipboard();\r
4722       break;\r
4723 \r
4724     case IDM_PasteGame:\r
4725       PasteGameFromClipboard();\r
4726       break;\r
4727 \r
4728     case IDM_CopyGameListToClipboard:\r
4729       CopyGameListToClipboard();\r
4730       break;\r
4731 \r
4732     /* [AS] Autodetect FEN or PGN data */\r
4733     case IDM_PasteAny:\r
4734       PasteGameOrFENFromClipboard();\r
4735       break;\r
4736 \r
4737     /* [AS] Move history */\r
4738     case IDM_ShowMoveHistory:\r
4739         if( MoveHistoryIsUp() ) {\r
4740             MoveHistoryPopDown();\r
4741         }\r
4742         else {\r
4743             MoveHistoryPopUp();\r
4744         }\r
4745         break;\r
4746 \r
4747     /* [AS] Eval graph */\r
4748     case IDM_ShowEvalGraph:\r
4749         if( EvalGraphIsUp() ) {\r
4750             EvalGraphPopDown();\r
4751         }\r
4752         else {\r
4753             EvalGraphPopUp();\r
4754             SetFocus(hwndMain);\r
4755         }\r
4756         break;\r
4757 \r
4758     /* [AS] Engine output */\r
4759     case IDM_ShowEngineOutput:\r
4760         if( EngineOutputIsUp() ) {\r
4761             EngineOutputPopDown();\r
4762         }\r
4763         else {\r
4764             EngineOutputPopUp();\r
4765         }\r
4766         break;\r
4767 \r
4768     /* [AS] User adjudication */\r
4769     case IDM_UserAdjudication_White:\r
4770         UserAdjudicationEvent( +1 );\r
4771         break;\r
4772 \r
4773     case IDM_UserAdjudication_Black:\r
4774         UserAdjudicationEvent( -1 );\r
4775         break;\r
4776 \r
4777     case IDM_UserAdjudication_Draw:\r
4778         UserAdjudicationEvent( 0 );\r
4779         break;\r
4780 \r
4781     /* [AS] Game list options dialog */\r
4782     case IDM_GameListOptions:\r
4783       GameListOptions();\r
4784       break;\r
4785 \r
4786     case IDM_NewChat:\r
4787       ChatPopUp(NULL);\r
4788       break;\r
4789 \r
4790     case IDM_CopyPosition:\r
4791       CopyFENToClipboard();\r
4792       break;\r
4793 \r
4794     case IDM_PastePosition:\r
4795       PasteFENFromClipboard();\r
4796       break;\r
4797 \r
4798     case IDM_MailMove:\r
4799       MailMoveEvent();\r
4800       break;\r
4801 \r
4802     case IDM_ReloadCMailMsg:\r
4803       Reset(TRUE, TRUE);\r
4804       ReloadCmailMsgEvent(FALSE);\r
4805       break;\r
4806 \r
4807     case IDM_Minimize:\r
4808       ShowWindow(hwnd, SW_MINIMIZE);\r
4809       break;\r
4810 \r
4811     case IDM_Exit:\r
4812       ExitEvent(0);\r
4813       break;\r
4814 \r
4815     case IDM_MachineWhite:\r
4816       MachineWhiteEvent();\r
4817       /*\r
4818        * refresh the tags dialog only if it's visible\r
4819        */\r
4820       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4821           char *tags;\r
4822           tags = PGNTags(&gameInfo);\r
4823           TagsPopUp(tags, CmailMsg());\r
4824           free(tags);\r
4825       }\r
4826       SAY("computer starts playing white");\r
4827       break;\r
4828 \r
4829     case IDM_MachineBlack:\r
4830       MachineBlackEvent();\r
4831       /*\r
4832        * refresh the tags dialog only if it's visible\r
4833        */\r
4834       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4835           char *tags;\r
4836           tags = PGNTags(&gameInfo);\r
4837           TagsPopUp(tags, CmailMsg());\r
4838           free(tags);\r
4839       }\r
4840       SAY("computer starts playing black");\r
4841       break;\r
4842 \r
4843     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4844       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4845         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4846       }\r
4847       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4848       appData.matchGames = appData.defaultMatchGames;\r
4849       matchGame = 1;\r
4850       first.matchWins = second.matchWins = 0;\r
4851 \r
4852     case IDM_TwoMachines:\r
4853       TwoMachinesEvent();\r
4854       /*\r
4855        * refresh the tags dialog only if it's visible\r
4856        */\r
4857       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4858           char *tags;\r
4859           tags = PGNTags(&gameInfo);\r
4860           TagsPopUp(tags, CmailMsg());\r
4861           free(tags);\r
4862       }\r
4863       SAY("computer starts playing both sides");\r
4864       break;\r
4865 \r
4866     case IDM_AnalysisMode:\r
4867       if (!first.analysisSupport) {\r
4868         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4869         DisplayError(buf, 0);\r
4870       } else {\r
4871         SAY("analyzing current position");\r
4872         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4873         if (appData.icsActive) {\r
4874                if (gameMode != IcsObserving) {\r
4875                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4876                        DisplayError(buf, 0);\r
4877                        /* secure check */\r
4878                        if (appData.icsEngineAnalyze) {\r
4879                                if (appData.debugMode) \r
4880                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4881                                ExitAnalyzeMode();\r
4882                                ModeHighlight();\r
4883                                break;\r
4884                        }\r
4885                        break;\r
4886                } else {\r
4887                        /* if enable, user want disable icsEngineAnalyze */\r
4888                        if (appData.icsEngineAnalyze) {\r
4889                                ExitAnalyzeMode();\r
4890                                ModeHighlight();\r
4891                                break;\r
4892                        }\r
4893                        appData.icsEngineAnalyze = TRUE;\r
4894                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4895                }\r
4896         } \r
4897         if (!appData.showThinking) ToggleShowThinking();\r
4898         AnalyzeModeEvent();\r
4899       }\r
4900       break;\r
4901 \r
4902     case IDM_AnalyzeFile:\r
4903       if (!first.analysisSupport) {\r
4904         char buf[MSG_SIZ];\r
4905           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4906         DisplayError(buf, 0);\r
4907       } else {\r
4908         if (!appData.showThinking) ToggleShowThinking();\r
4909         AnalyzeFileEvent();\r
4910         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4911         AnalysisPeriodicEvent(1);\r
4912       }\r
4913       break;\r
4914 \r
4915     case IDM_IcsClient:\r
4916       IcsClientEvent();\r
4917       break;\r
4918 \r
4919     case IDM_EditGame:\r
4920     case IDM_EditGame2:\r
4921       EditGameEvent();\r
4922       SAY("edit game");\r
4923       break;\r
4924 \r
4925     case IDM_EditPosition:\r
4926     case IDM_EditPosition2:\r
4927       EditPositionEvent();\r
4928       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4929       break;\r
4930 \r
4931     case IDM_Training:\r
4932       TrainingEvent();\r
4933       break;\r
4934 \r
4935     case IDM_ShowGameList:\r
4936       ShowGameListProc();\r
4937       break;\r
4938 \r
4939     case IDM_EditProgs1:\r
4940       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4941       break;\r
4942 \r
4943     case IDM_EditProgs2:\r
4944       EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4945       break;\r
4946 \r
4947     case IDM_EditServers:\r
4948       EditTagsPopUp(icsNames, &icsNames);\r
4949       break;\r
4950 \r
4951     case IDM_EditTags:\r
4952     case IDM_Tags:\r
4953       EditTagsProc();\r
4954       break;\r
4955 \r
4956     case IDM_EditComment:\r
4957     case IDM_Comment:\r
4958       if (commentUp && editComment) {\r
4959         CommentPopDown();\r
4960       } else {\r
4961         EditCommentEvent();\r
4962       }\r
4963       break;\r
4964 \r
4965     case IDM_Pause:\r
4966       PauseEvent();\r
4967       break;\r
4968 \r
4969     case IDM_Accept:\r
4970       AcceptEvent();\r
4971       break;\r
4972 \r
4973     case IDM_Decline:\r
4974       DeclineEvent();\r
4975       break;\r
4976 \r
4977     case IDM_Rematch:\r
4978       RematchEvent();\r
4979       break;\r
4980 \r
4981     case IDM_CallFlag:\r
4982       CallFlagEvent();\r
4983       break;\r
4984 \r
4985     case IDM_Draw:\r
4986       DrawEvent();\r
4987       break;\r
4988 \r
4989     case IDM_Adjourn:\r
4990       AdjournEvent();\r
4991       break;\r
4992 \r
4993     case IDM_Abort:\r
4994       AbortEvent();\r
4995       break;\r
4996 \r
4997     case IDM_Resign:\r
4998       ResignEvent();\r
4999       break;\r
5000 \r
5001     case IDM_StopObserving:\r
5002       StopObservingEvent();\r
5003       break;\r
5004 \r
5005     case IDM_StopExamining:\r
5006       StopExaminingEvent();\r
5007       break;\r
5008 \r
5009     case IDM_Upload:\r
5010       UploadGameEvent();\r
5011       break;\r
5012 \r
5013     case IDM_TypeInMove:\r
5014       PopUpMoveDialog('\000');\r
5015       break;\r
5016 \r
5017     case IDM_TypeInName:\r
5018       PopUpNameDialog('\000');\r
5019       break;\r
5020 \r
5021     case IDM_Backward:\r
5022       BackwardEvent();\r
5023       SetFocus(hwndMain);\r
5024       break;\r
5025 \r
5026     JAWS_MENU_ITEMS\r
5027 \r
5028     case IDM_Forward:\r
5029       ForwardEvent();\r
5030       SetFocus(hwndMain);\r
5031       break;\r
5032 \r
5033     case IDM_ToStart:\r
5034       ToStartEvent();\r
5035       SetFocus(hwndMain);\r
5036       break;\r
5037 \r
5038     case IDM_ToEnd:\r
5039       ToEndEvent();\r
5040       SetFocus(hwndMain);\r
5041       break;\r
5042 \r
5043     case IDM_Revert:\r
5044       RevertEvent(FALSE);\r
5045       break;\r
5046 \r
5047     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5048       RevertEvent(TRUE);\r
5049       break;\r
5050 \r
5051     case IDM_TruncateGame:\r
5052       TruncateGameEvent();\r
5053       break;\r
5054 \r
5055     case IDM_MoveNow:\r
5056       MoveNowEvent();\r
5057       break;\r
5058 \r
5059     case IDM_RetractMove:\r
5060       RetractMoveEvent();\r
5061       break;\r
5062 \r
5063     case IDM_FlipView:\r
5064       flipView = !flipView;\r
5065       DrawPosition(FALSE, NULL);\r
5066       break;\r
5067 \r
5068     case IDM_FlipClock:\r
5069       flipClock = !flipClock;\r
5070       DisplayBothClocks();\r
5071       DrawPosition(FALSE, NULL);\r
5072       break;\r
5073 \r
5074     case IDM_MuteSounds:\r
5075       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5076       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5077                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5078       break;\r
5079 \r
5080     case IDM_GeneralOptions:\r
5081       GeneralOptionsPopup(hwnd);\r
5082       DrawPosition(TRUE, NULL);\r
5083       break;\r
5084 \r
5085     case IDM_BoardOptions:\r
5086       BoardOptionsPopup(hwnd);\r
5087       break;\r
5088 \r
5089     case IDM_EnginePlayOptions:\r
5090       EnginePlayOptionsPopup(hwnd);\r
5091       break;\r
5092 \r
5093     case IDM_Engine1Options:\r
5094       EngineOptionsPopup(hwnd, &first);\r
5095       break;\r
5096 \r
5097     case IDM_Engine2Options:\r
5098       savedHwnd = hwnd;\r
5099       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5100       EngineOptionsPopup(hwnd, &second);\r
5101       break;\r
5102 \r
5103     case IDM_OptionsUCI:\r
5104       UciOptionsPopup(hwnd);\r
5105       break;\r
5106 \r
5107     case IDM_IcsOptions:\r
5108       IcsOptionsPopup(hwnd);\r
5109       break;\r
5110 \r
5111     case IDM_Fonts:\r
5112       FontsOptionsPopup(hwnd);\r
5113       break;\r
5114 \r
5115     case IDM_Sounds:\r
5116       SoundOptionsPopup(hwnd);\r
5117       break;\r
5118 \r
5119     case IDM_CommPort:\r
5120       CommPortOptionsPopup(hwnd);\r
5121       break;\r
5122 \r
5123     case IDM_LoadOptions:\r
5124       LoadOptionsPopup(hwnd);\r
5125       break;\r
5126 \r
5127     case IDM_SaveOptions:\r
5128       SaveOptionsPopup(hwnd);\r
5129       break;\r
5130 \r
5131     case IDM_TimeControl:\r
5132       TimeControlOptionsPopup(hwnd);\r
5133       break;\r
5134 \r
5135     case IDM_SaveSettings:\r
5136       SaveSettings(settingsFileName);\r
5137       break;\r
5138 \r
5139     case IDM_SaveSettingsOnExit:\r
5140       saveSettingsOnExit = !saveSettingsOnExit;\r
5141       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5142                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5143                                          MF_CHECKED : MF_UNCHECKED));\r
5144       break;\r
5145 \r
5146     case IDM_Hint:\r
5147       HintEvent();\r
5148       break;\r
5149 \r
5150     case IDM_Book:\r
5151       BookEvent();\r
5152       break;\r
5153 \r
5154     case IDM_AboutGame:\r
5155       AboutGameEvent();\r
5156       break;\r
5157 \r
5158     case IDM_Debug:\r
5159       appData.debugMode = !appData.debugMode;\r
5160       if (appData.debugMode) {\r
5161         char dir[MSG_SIZ];\r
5162         GetCurrentDirectory(MSG_SIZ, dir);\r
5163         SetCurrentDirectory(installDir);\r
5164         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5165         SetCurrentDirectory(dir);\r
5166         setbuf(debugFP, NULL);\r
5167       } else {\r
5168         fclose(debugFP);\r
5169         debugFP = NULL;\r
5170       }\r
5171       break;\r
5172 \r
5173     case IDM_HELPCONTENTS:\r
5174       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5175           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5176           MessageBox (GetFocus(),\r
5177                     _("Unable to activate help"),\r
5178                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5179       }\r
5180       break;\r
5181 \r
5182     case IDM_HELPSEARCH:\r
5183         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5184             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5185         MessageBox (GetFocus(),\r
5186                     _("Unable to activate help"),\r
5187                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5188       }\r
5189       break;\r
5190 \r
5191     case IDM_HELPHELP:\r
5192       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5193         MessageBox (GetFocus(),\r
5194                     _("Unable to activate help"),\r
5195                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5196       }\r
5197       break;\r
5198 \r
5199     case IDM_ABOUT:\r
5200       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5201       DialogBox(hInst, \r
5202         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5203         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5204       FreeProcInstance(lpProc);\r
5205       break;\r
5206 \r
5207     case IDM_DirectCommand1:\r
5208       AskQuestionEvent(_("Direct Command"),\r
5209                        _("Send to chess program:"), "", "1");\r
5210       break;\r
5211     case IDM_DirectCommand2:\r
5212       AskQuestionEvent(_("Direct Command"),\r
5213                        _("Send to second chess program:"), "", "2");\r
5214       break;\r
5215 \r
5216     case EP_WhitePawn:\r
5217       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5218       fromX = fromY = -1;\r
5219       break;\r
5220 \r
5221     case EP_WhiteKnight:\r
5222       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5223       fromX = fromY = -1;\r
5224       break;\r
5225 \r
5226     case EP_WhiteBishop:\r
5227       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5228       fromX = fromY = -1;\r
5229       break;\r
5230 \r
5231     case EP_WhiteRook:\r
5232       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5233       fromX = fromY = -1;\r
5234       break;\r
5235 \r
5236     case EP_WhiteQueen:\r
5237       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5238       fromX = fromY = -1;\r
5239       break;\r
5240 \r
5241     case EP_WhiteFerz:\r
5242       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5243       fromX = fromY = -1;\r
5244       break;\r
5245 \r
5246     case EP_WhiteWazir:\r
5247       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5248       fromX = fromY = -1;\r
5249       break;\r
5250 \r
5251     case EP_WhiteAlfil:\r
5252       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5253       fromX = fromY = -1;\r
5254       break;\r
5255 \r
5256     case EP_WhiteCannon:\r
5257       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5258       fromX = fromY = -1;\r
5259       break;\r
5260 \r
5261     case EP_WhiteCardinal:\r
5262       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5263       fromX = fromY = -1;\r
5264       break;\r
5265 \r
5266     case EP_WhiteMarshall:\r
5267       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5268       fromX = fromY = -1;\r
5269       break;\r
5270 \r
5271     case EP_WhiteKing:\r
5272       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5273       fromX = fromY = -1;\r
5274       break;\r
5275 \r
5276     case EP_BlackPawn:\r
5277       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5278       fromX = fromY = -1;\r
5279       break;\r
5280 \r
5281     case EP_BlackKnight:\r
5282       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5283       fromX = fromY = -1;\r
5284       break;\r
5285 \r
5286     case EP_BlackBishop:\r
5287       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5288       fromX = fromY = -1;\r
5289       break;\r
5290 \r
5291     case EP_BlackRook:\r
5292       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5293       fromX = fromY = -1;\r
5294       break;\r
5295 \r
5296     case EP_BlackQueen:\r
5297       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5298       fromX = fromY = -1;\r
5299       break;\r
5300 \r
5301     case EP_BlackFerz:\r
5302       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5303       fromX = fromY = -1;\r
5304       break;\r
5305 \r
5306     case EP_BlackWazir:\r
5307       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5308       fromX = fromY = -1;\r
5309       break;\r
5310 \r
5311     case EP_BlackAlfil:\r
5312       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5313       fromX = fromY = -1;\r
5314       break;\r
5315 \r
5316     case EP_BlackCannon:\r
5317       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5318       fromX = fromY = -1;\r
5319       break;\r
5320 \r
5321     case EP_BlackCardinal:\r
5322       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5323       fromX = fromY = -1;\r
5324       break;\r
5325 \r
5326     case EP_BlackMarshall:\r
5327       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5328       fromX = fromY = -1;\r
5329       break;\r
5330 \r
5331     case EP_BlackKing:\r
5332       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5333       fromX = fromY = -1;\r
5334       break;\r
5335 \r
5336     case EP_EmptySquare:\r
5337       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5338       fromX = fromY = -1;\r
5339       break;\r
5340 \r
5341     case EP_ClearBoard:\r
5342       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5343       fromX = fromY = -1;\r
5344       break;\r
5345 \r
5346     case EP_White:\r
5347       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5348       fromX = fromY = -1;\r
5349       break;\r
5350 \r
5351     case EP_Black:\r
5352       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5353       fromX = fromY = -1;\r
5354       break;\r
5355 \r
5356     case EP_Promote:\r
5357       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5358       fromX = fromY = -1;\r
5359       break;\r
5360 \r
5361     case EP_Demote:\r
5362       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5363       fromX = fromY = -1;\r
5364       break;\r
5365 \r
5366     case DP_Pawn:\r
5367       DropMenuEvent(WhitePawn, fromX, fromY);\r
5368       fromX = fromY = -1;\r
5369       break;\r
5370 \r
5371     case DP_Knight:\r
5372       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5373       fromX = fromY = -1;\r
5374       break;\r
5375 \r
5376     case DP_Bishop:\r
5377       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5378       fromX = fromY = -1;\r
5379       break;\r
5380 \r
5381     case DP_Rook:\r
5382       DropMenuEvent(WhiteRook, fromX, fromY);\r
5383       fromX = fromY = -1;\r
5384       break;\r
5385 \r
5386     case DP_Queen:\r
5387       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5388       fromX = fromY = -1;\r
5389       break;\r
5390 \r
5391     case IDM_English:\r
5392       barbaric = 0;\r
5393       TranslateMenus(0);\r
5394       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5395       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5396       lastChecked = wmId;\r
5397       break;\r
5398 \r
5399     default:\r
5400       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5401           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5402           TranslateMenus(0);\r
5403           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5404           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5405           lastChecked = wmId;\r
5406           break;\r
5407       }\r
5408       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5409     }\r
5410     break;\r
5411 \r
5412   case WM_TIMER:\r
5413     switch (wParam) {\r
5414     case CLOCK_TIMER_ID:\r
5415       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5416       clockTimerEvent = 0;\r
5417       DecrementClocks(); /* call into back end */\r
5418       break;\r
5419     case LOAD_GAME_TIMER_ID:\r
5420       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5421       loadGameTimerEvent = 0;\r
5422       AutoPlayGameLoop(); /* call into back end */\r
5423       break;\r
5424     case ANALYSIS_TIMER_ID:\r
5425       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5426                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5427         AnalysisPeriodicEvent(0);\r
5428       } else {\r
5429         KillTimer(hwnd, analysisTimerEvent);\r
5430         analysisTimerEvent = 0;\r
5431       }\r
5432       break;\r
5433     case DELAYED_TIMER_ID:\r
5434       KillTimer(hwnd, delayedTimerEvent);\r
5435       delayedTimerEvent = 0;\r
5436       delayedTimerCallback();\r
5437       break;\r
5438     }\r
5439     break;\r
5440 \r
5441   case WM_USER_Input:\r
5442     InputEvent(hwnd, message, wParam, lParam);\r
5443     break;\r
5444 \r
5445   /* [AS] Also move "attached" child windows */\r
5446   case WM_WINDOWPOSCHANGING:\r
5447 \r
5448     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5449         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5450 \r
5451         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5452             /* Window is moving */\r
5453             RECT rcMain;\r
5454 \r
5455 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5456             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5457             rcMain.right  = wpMain.x + wpMain.width;\r
5458             rcMain.top    = wpMain.y;\r
5459             rcMain.bottom = wpMain.y + wpMain.height;\r
5460             \r
5461             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5462             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5463             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5464             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5465             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5466             wpMain.x = lpwp->x;\r
5467             wpMain.y = lpwp->y;\r
5468         }\r
5469     }\r
5470     break;\r
5471 \r
5472   /* [AS] Snapping */\r
5473   case WM_ENTERSIZEMOVE:\r
5474     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5475     if (hwnd == hwndMain) {\r
5476       doingSizing = TRUE;\r
5477       lastSizing = 0;\r
5478     }\r
5479     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5480     break;\r
5481 \r
5482   case WM_SIZING:\r
5483     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5484     if (hwnd == hwndMain) {\r
5485       lastSizing = wParam;\r
5486     }\r
5487     break;\r
5488 \r
5489   case WM_MOVING:\r
5490     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5491       return OnMoving( &sd, hwnd, wParam, lParam );\r
5492 \r
5493   case WM_EXITSIZEMOVE:\r
5494     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5495     if (hwnd == hwndMain) {\r
5496       RECT client;\r
5497       doingSizing = FALSE;\r
5498       InvalidateRect(hwnd, &boardRect, FALSE);\r
5499       GetClientRect(hwnd, &client);\r
5500       ResizeBoard(client.right, client.bottom, lastSizing);\r
5501       lastSizing = 0;\r
5502       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5503     }\r
5504     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5505     break;\r
5506 \r
5507   case WM_DESTROY: /* message: window being destroyed */\r
5508     PostQuitMessage(0);\r
5509     break;\r
5510 \r
5511   case WM_CLOSE:\r
5512     if (hwnd == hwndMain) {\r
5513       ExitEvent(0);\r
5514     }\r
5515     break;\r
5516 \r
5517   default:      /* Passes it on if unprocessed */\r
5518     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5519   }\r
5520   return 0;\r
5521 }\r
5522 \r
5523 /*---------------------------------------------------------------------------*\\r
5524  *\r
5525  * Misc utility routines\r
5526  *\r
5527 \*---------------------------------------------------------------------------*/\r
5528 \r
5529 /*\r
5530  * Decent random number generator, at least not as bad as Windows\r
5531  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5532  */\r
5533 unsigned int randstate;\r
5534 \r
5535 int\r
5536 myrandom(void)\r
5537 {\r
5538   randstate = randstate * 1664525 + 1013904223;\r
5539   return (int) randstate & 0x7fffffff;\r
5540 }\r
5541 \r
5542 void\r
5543 mysrandom(unsigned int seed)\r
5544 {\r
5545   randstate = seed;\r
5546 }\r
5547 \r
5548 \r
5549 /* \r
5550  * returns TRUE if user selects a different color, FALSE otherwise \r
5551  */\r
5552 \r
5553 BOOL\r
5554 ChangeColor(HWND hwnd, COLORREF *which)\r
5555 {\r
5556   static BOOL firstTime = TRUE;\r
5557   static DWORD customColors[16];\r
5558   CHOOSECOLOR cc;\r
5559   COLORREF newcolor;\r
5560   int i;\r
5561   ColorClass ccl;\r
5562 \r
5563   if (firstTime) {\r
5564     /* Make initial colors in use available as custom colors */\r
5565     /* Should we put the compiled-in defaults here instead? */\r
5566     i = 0;\r
5567     customColors[i++] = lightSquareColor & 0xffffff;\r
5568     customColors[i++] = darkSquareColor & 0xffffff;\r
5569     customColors[i++] = whitePieceColor & 0xffffff;\r
5570     customColors[i++] = blackPieceColor & 0xffffff;\r
5571     customColors[i++] = highlightSquareColor & 0xffffff;\r
5572     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5573 \r
5574     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5575       customColors[i++] = textAttribs[ccl].color;\r
5576     }\r
5577     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5578     firstTime = FALSE;\r
5579   }\r
5580 \r
5581   cc.lStructSize = sizeof(cc);\r
5582   cc.hwndOwner = hwnd;\r
5583   cc.hInstance = NULL;\r
5584   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5585   cc.lpCustColors = (LPDWORD) customColors;\r
5586   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5587 \r
5588   if (!ChooseColor(&cc)) return FALSE;\r
5589 \r
5590   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5591   if (newcolor == *which) return FALSE;\r
5592   *which = newcolor;\r
5593   return TRUE;\r
5594 \r
5595   /*\r
5596   InitDrawingColors();\r
5597   InvalidateRect(hwnd, &boardRect, FALSE);\r
5598   */\r
5599 }\r
5600 \r
5601 BOOLEAN\r
5602 MyLoadSound(MySound *ms)\r
5603 {\r
5604   BOOL ok = FALSE;\r
5605   struct stat st;\r
5606   FILE *f;\r
5607 \r
5608   if (ms->data) free(ms->data);\r
5609   ms->data = NULL;\r
5610 \r
5611   switch (ms->name[0]) {\r
5612   case NULLCHAR:\r
5613     /* Silence */\r
5614     ok = TRUE;\r
5615     break;\r
5616   case '$':\r
5617     /* System sound from Control Panel.  Don't preload here. */\r
5618     ok = TRUE;\r
5619     break;\r
5620   case '!':\r
5621     if (ms->name[1] == NULLCHAR) {\r
5622       /* "!" alone = silence */\r
5623       ok = TRUE;\r
5624     } else {\r
5625       /* Builtin wave resource.  Error if not found. */\r
5626       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5627       if (h == NULL) break;\r
5628       ms->data = (void *)LoadResource(hInst, h);\r
5629       if (h == NULL) break;\r
5630       ok = TRUE;\r
5631     }\r
5632     break;\r
5633   default:\r
5634     /* .wav file.  Error if not found. */\r
5635     f = fopen(ms->name, "rb");\r
5636     if (f == NULL) break;\r
5637     if (fstat(fileno(f), &st) < 0) break;\r
5638     ms->data = malloc(st.st_size);\r
5639     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5640     fclose(f);\r
5641     ok = TRUE;\r
5642     break;\r
5643   }\r
5644   if (!ok) {\r
5645     char buf[MSG_SIZ];\r
5646       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5647     DisplayError(buf, GetLastError());\r
5648   }\r
5649   return ok;\r
5650 }\r
5651 \r
5652 BOOLEAN\r
5653 MyPlaySound(MySound *ms)\r
5654 {\r
5655   BOOLEAN ok = FALSE;\r
5656 \r
5657   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5658   switch (ms->name[0]) {\r
5659   case NULLCHAR:\r
5660         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5661     /* Silence */\r
5662     ok = TRUE;\r
5663     break;\r
5664   case '$':\r
5665     /* System sound from Control Panel (deprecated feature).\r
5666        "$" alone or an unset sound name gets default beep (still in use). */\r
5667     if (ms->name[1]) {\r
5668       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5669     }\r
5670     if (!ok) ok = MessageBeep(MB_OK);\r
5671     break; \r
5672   case '!':\r
5673     /* Builtin wave resource, or "!" alone for silence */\r
5674     if (ms->name[1]) {\r
5675       if (ms->data == NULL) return FALSE;\r
5676       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5677     } else {\r
5678       ok = TRUE;\r
5679     }\r
5680     break;\r
5681   default:\r
5682     /* .wav file.  Error if not found. */\r
5683     if (ms->data == NULL) return FALSE;\r
5684     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5685     break;\r
5686   }\r
5687   /* Don't print an error: this can happen innocently if the sound driver\r
5688      is busy; for instance, if another instance of WinBoard is playing\r
5689      a sound at about the same time. */\r
5690   return ok;\r
5691 }\r
5692 \r
5693 \r
5694 LRESULT CALLBACK\r
5695 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5696 {\r
5697   BOOL ok;\r
5698   OPENFILENAME *ofn;\r
5699   static UINT *number; /* gross that this is static */\r
5700 \r
5701   switch (message) {\r
5702   case WM_INITDIALOG: /* message: initialize dialog box */\r
5703     /* Center the dialog over the application window */\r
5704     ofn = (OPENFILENAME *) lParam;\r
5705     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5706       number = (UINT *) ofn->lCustData;\r
5707       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5708     } else {\r
5709       number = NULL;\r
5710     }\r
5711     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5712     Translate(hDlg, 1536);\r
5713     return FALSE;  /* Allow for further processing */\r
5714 \r
5715   case WM_COMMAND:\r
5716     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5717       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5718     }\r
5719     return FALSE;  /* Allow for further processing */\r
5720   }\r
5721   return FALSE;\r
5722 }\r
5723 \r
5724 UINT APIENTRY\r
5725 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5726 {\r
5727   static UINT *number;\r
5728   OPENFILENAME *ofname;\r
5729   OFNOTIFY *ofnot;\r
5730   switch (uiMsg) {\r
5731   case WM_INITDIALOG:\r
5732     Translate(hdlg, DLG_IndexNumber);\r
5733     ofname = (OPENFILENAME *)lParam;\r
5734     number = (UINT *)(ofname->lCustData);\r
5735     break;\r
5736   case WM_NOTIFY:\r
5737     ofnot = (OFNOTIFY *)lParam;\r
5738     if (ofnot->hdr.code == CDN_FILEOK) {\r
5739       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5740     }\r
5741     break;\r
5742   }\r
5743   return 0;\r
5744 }\r
5745 \r
5746 \r
5747 FILE *\r
5748 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5749                char *nameFilt, char *dlgTitle, UINT *number,\r
5750                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5751 {\r
5752   OPENFILENAME openFileName;\r
5753   char buf1[MSG_SIZ];\r
5754   FILE *f;\r
5755 \r
5756   if (fileName == NULL) fileName = buf1;\r
5757   if (defName == NULL) {\r
5758     safeStrCpy(fileName, "*.", 3 );\r
5759     strcat(fileName, defExt);\r
5760   } else {\r
5761     safeStrCpy(fileName, defName, MSG_SIZ );\r
5762   }\r
5763     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5764   if (number) *number = 0;\r
5765 \r
5766   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5767   openFileName.hwndOwner         = hwnd;\r
5768   openFileName.hInstance         = (HANDLE) hInst;\r
5769   openFileName.lpstrFilter       = nameFilt;\r
5770   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5771   openFileName.nMaxCustFilter    = 0L;\r
5772   openFileName.nFilterIndex      = 1L;\r
5773   openFileName.lpstrFile         = fileName;\r
5774   openFileName.nMaxFile          = MSG_SIZ;\r
5775   openFileName.lpstrFileTitle    = fileTitle;\r
5776   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5777   openFileName.lpstrInitialDir   = NULL;\r
5778   openFileName.lpstrTitle        = dlgTitle;\r
5779   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5780     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5781     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5782     | (oldDialog ? 0 : OFN_EXPLORER);\r
5783   openFileName.nFileOffset       = 0;\r
5784   openFileName.nFileExtension    = 0;\r
5785   openFileName.lpstrDefExt       = defExt;\r
5786   openFileName.lCustData         = (LONG) number;\r
5787   openFileName.lpfnHook          = oldDialog ?\r
5788     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5789   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5790 \r
5791   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5792                         GetOpenFileName(&openFileName)) {\r
5793     /* open the file */\r
5794     f = fopen(openFileName.lpstrFile, write);\r
5795     if (f == NULL) {\r
5796       MessageBox(hwnd, _("File open failed"), NULL,\r
5797                  MB_OK|MB_ICONEXCLAMATION);\r
5798       return NULL;\r
5799     }\r
5800   } else {\r
5801     int err = CommDlgExtendedError();\r
5802     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5803     return FALSE;\r
5804   }\r
5805   return f;\r
5806 }\r
5807 \r
5808 \r
5809 \r
5810 VOID APIENTRY\r
5811 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5812 {\r
5813   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5814 \r
5815   /*\r
5816    * Get the first pop-up menu in the menu template. This is the\r
5817    * menu that TrackPopupMenu displays.\r
5818    */\r
5819   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5820   TranslateOneMenu(10, hmenuTrackPopup);\r
5821 \r
5822   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5823 \r
5824   /*\r
5825    * TrackPopup uses screen coordinates, so convert the\r
5826    * coordinates of the mouse click to screen coordinates.\r
5827    */\r
5828   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5829 \r
5830   /* Draw and track the floating pop-up menu. */\r
5831   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5832                  pt.x, pt.y, 0, hwnd, NULL);\r
5833 \r
5834   /* Destroy the menu.*/\r
5835   DestroyMenu(hmenu);\r
5836 }\r
5837    \r
5838 typedef struct {\r
5839   HWND hDlg, hText;\r
5840   int sizeX, sizeY, newSizeX, newSizeY;\r
5841   HDWP hdwp;\r
5842 } ResizeEditPlusButtonsClosure;\r
5843 \r
5844 BOOL CALLBACK\r
5845 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5846 {\r
5847   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5848   RECT rect;\r
5849   POINT pt;\r
5850 \r
5851   if (hChild == cl->hText) return TRUE;\r
5852   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5853   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5854   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5855   ScreenToClient(cl->hDlg, &pt);\r
5856   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5857     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5858   return TRUE;\r
5859 }\r
5860 \r
5861 /* Resize a dialog that has a (rich) edit field filling most of\r
5862    the top, with a row of buttons below */\r
5863 VOID\r
5864 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5865 {\r
5866   RECT rectText;\r
5867   int newTextHeight, newTextWidth;\r
5868   ResizeEditPlusButtonsClosure cl;\r
5869   \r
5870   /*if (IsIconic(hDlg)) return;*/\r
5871   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5872   \r
5873   cl.hdwp = BeginDeferWindowPos(8);\r
5874 \r
5875   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5876   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5877   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5878   if (newTextHeight < 0) {\r
5879     newSizeY += -newTextHeight;\r
5880     newTextHeight = 0;\r
5881   }\r
5882   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5883     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5884 \r
5885   cl.hDlg = hDlg;\r
5886   cl.hText = hText;\r
5887   cl.sizeX = sizeX;\r
5888   cl.sizeY = sizeY;\r
5889   cl.newSizeX = newSizeX;\r
5890   cl.newSizeY = newSizeY;\r
5891   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5892 \r
5893   EndDeferWindowPos(cl.hdwp);\r
5894 }\r
5895 \r
5896 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5897 {\r
5898     RECT    rChild, rParent;\r
5899     int     wChild, hChild, wParent, hParent;\r
5900     int     wScreen, hScreen, xNew, yNew;\r
5901     HDC     hdc;\r
5902 \r
5903     /* Get the Height and Width of the child window */\r
5904     GetWindowRect (hwndChild, &rChild);\r
5905     wChild = rChild.right - rChild.left;\r
5906     hChild = rChild.bottom - rChild.top;\r
5907 \r
5908     /* Get the Height and Width of the parent window */\r
5909     GetWindowRect (hwndParent, &rParent);\r
5910     wParent = rParent.right - rParent.left;\r
5911     hParent = rParent.bottom - rParent.top;\r
5912 \r
5913     /* Get the display limits */\r
5914     hdc = GetDC (hwndChild);\r
5915     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5916     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5917     ReleaseDC(hwndChild, hdc);\r
5918 \r
5919     /* Calculate new X position, then adjust for screen */\r
5920     xNew = rParent.left + ((wParent - wChild) /2);\r
5921     if (xNew < 0) {\r
5922         xNew = 0;\r
5923     } else if ((xNew+wChild) > wScreen) {\r
5924         xNew = wScreen - wChild;\r
5925     }\r
5926 \r
5927     /* Calculate new Y position, then adjust for screen */\r
5928     if( mode == 0 ) {\r
5929         yNew = rParent.top  + ((hParent - hChild) /2);\r
5930     }\r
5931     else {\r
5932         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5933     }\r
5934 \r
5935     if (yNew < 0) {\r
5936         yNew = 0;\r
5937     } else if ((yNew+hChild) > hScreen) {\r
5938         yNew = hScreen - hChild;\r
5939     }\r
5940 \r
5941     /* Set it, and return */\r
5942     return SetWindowPos (hwndChild, NULL,\r
5943                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5944 }\r
5945 \r
5946 /* Center one window over another */\r
5947 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5948 {\r
5949     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5950 }\r
5951 \r
5952 /*---------------------------------------------------------------------------*\\r
5953  *\r
5954  * Startup Dialog functions\r
5955  *\r
5956 \*---------------------------------------------------------------------------*/\r
5957 void\r
5958 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5959 {\r
5960   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5961 \r
5962   while (*cd != NULL) {\r
5963     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5964     cd++;\r
5965   }\r
5966 }\r
5967 \r
5968 void\r
5969 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5970 {\r
5971   char buf1[MAX_ARG_LEN];\r
5972   int len;\r
5973 \r
5974   if (str[0] == '@') {\r
5975     FILE* f = fopen(str + 1, "r");\r
5976     if (f == NULL) {\r
5977       DisplayFatalError(str + 1, errno, 2);\r
5978       return;\r
5979     }\r
5980     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5981     fclose(f);\r
5982     buf1[len] = NULLCHAR;\r
5983     str = buf1;\r
5984   }\r
5985 \r
5986   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5987 \r
5988   for (;;) {\r
5989     char buf[MSG_SIZ];\r
5990     char *end = strchr(str, '\n');\r
5991     if (end == NULL) return;\r
5992     memcpy(buf, str, end - str);\r
5993     buf[end - str] = NULLCHAR;\r
5994     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5995     str = end + 1;\r
5996   }\r
5997 }\r
5998 \r
5999 void\r
6000 SetStartupDialogEnables(HWND hDlg)\r
6001 {\r
6002   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6003     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6004     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6005   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6006     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6007   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6008     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6009   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6010     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6011   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6012     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6013     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6014     IsDlgButtonChecked(hDlg, OPT_View));\r
6015 }\r
6016 \r
6017 char *\r
6018 QuoteForFilename(char *filename)\r
6019 {\r
6020   int dquote, space;\r
6021   dquote = strchr(filename, '"') != NULL;\r
6022   space = strchr(filename, ' ') != NULL;\r
6023   if (dquote || space) {\r
6024     if (dquote) {\r
6025       return "'";\r
6026     } else {\r
6027       return "\"";\r
6028     }\r
6029   } else {\r
6030     return "";\r
6031   }\r
6032 }\r
6033 \r
6034 VOID\r
6035 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6036 {\r
6037   char buf[MSG_SIZ];\r
6038   char *q;\r
6039 \r
6040   InitComboStringsFromOption(hwndCombo, nthnames);\r
6041   q = QuoteForFilename(nthcp);\r
6042     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6043   if (*nthdir != NULLCHAR) {\r
6044     q = QuoteForFilename(nthdir);\r
6045       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6046   }\r
6047   if (*nthcp == NULLCHAR) {\r
6048     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6049   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6050     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6051     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6052   }\r
6053 }\r
6054 \r
6055 LRESULT CALLBACK\r
6056 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6057 {\r
6058   char buf[MSG_SIZ];\r
6059   HANDLE hwndCombo;\r
6060   char *p;\r
6061 \r
6062   switch (message) {\r
6063   case WM_INITDIALOG:\r
6064     /* Center the dialog */\r
6065     CenterWindow (hDlg, GetDesktopWindow());\r
6066     Translate(hDlg, DLG_Startup);\r
6067     /* Initialize the dialog items */\r
6068     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6069                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6070                   firstChessProgramNames);\r
6071     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6072                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6073                   secondChessProgramNames);\r
6074     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6075     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6076       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6077     if (*appData.icsHelper != NULLCHAR) {\r
6078       char *q = QuoteForFilename(appData.icsHelper);\r
6079       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6080     }\r
6081     if (*appData.icsHost == NULLCHAR) {\r
6082       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6083       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6084     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6085       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6086       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6087     }\r
6088 \r
6089     if (appData.icsActive) {\r
6090       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6091     }\r
6092     else if (appData.noChessProgram) {\r
6093       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6094     }\r
6095     else {\r
6096       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6097     }\r
6098 \r
6099     SetStartupDialogEnables(hDlg);\r
6100     return TRUE;\r
6101 \r
6102   case WM_COMMAND:\r
6103     switch (LOWORD(wParam)) {\r
6104     case IDOK:\r
6105       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6106         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6107         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6108         p = buf;\r
6109         ParseArgs(StringGet, &p);\r
6110         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6111         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6112         p = buf;\r
6113         ParseArgs(StringGet, &p);\r
6114         appData.noChessProgram = FALSE;\r
6115         appData.icsActive = FALSE;\r
6116       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6117         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6118         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6119         p = buf;\r
6120         ParseArgs(StringGet, &p);\r
6121         if (appData.zippyPlay) {\r
6122           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6123           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6124           p = buf;\r
6125           ParseArgs(StringGet, &p);\r
6126         }\r
6127       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6128         appData.noChessProgram = TRUE;\r
6129         appData.icsActive = FALSE;\r
6130       } else {\r
6131         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6132                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6133         return TRUE;\r
6134       }\r
6135       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6136         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6137         p = buf;\r
6138         ParseArgs(StringGet, &p);\r
6139       }\r
6140       EndDialog(hDlg, TRUE);\r
6141       return TRUE;\r
6142 \r
6143     case IDCANCEL:\r
6144       ExitEvent(0);\r
6145       return TRUE;\r
6146 \r
6147     case IDM_HELPCONTENTS:\r
6148       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6149         MessageBox (GetFocus(),\r
6150                     _("Unable to activate help"),\r
6151                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6152       }\r
6153       break;\r
6154 \r
6155     default:\r
6156       SetStartupDialogEnables(hDlg);\r
6157       break;\r
6158     }\r
6159     break;\r
6160   }\r
6161   return FALSE;\r
6162 }\r
6163 \r
6164 /*---------------------------------------------------------------------------*\\r
6165  *\r
6166  * About box dialog functions\r
6167  *\r
6168 \*---------------------------------------------------------------------------*/\r
6169 \r
6170 /* Process messages for "About" dialog box */\r
6171 LRESULT CALLBACK\r
6172 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6173 {\r
6174   switch (message) {\r
6175   case WM_INITDIALOG: /* message: initialize dialog box */\r
6176     /* Center the dialog over the application window */\r
6177     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6178     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6179     Translate(hDlg, ABOUTBOX);\r
6180     JAWS_COPYRIGHT\r
6181     return (TRUE);\r
6182 \r
6183   case WM_COMMAND: /* message: received a command */\r
6184     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6185         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6186       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6187       return (TRUE);\r
6188     }\r
6189     break;\r
6190   }\r
6191   return (FALSE);\r
6192 }\r
6193 \r
6194 /*---------------------------------------------------------------------------*\\r
6195  *\r
6196  * Comment Dialog functions\r
6197  *\r
6198 \*---------------------------------------------------------------------------*/\r
6199 \r
6200 LRESULT CALLBACK\r
6201 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6202 {\r
6203   static HANDLE hwndText = NULL;\r
6204   int len, newSizeX, newSizeY, flags;\r
6205   static int sizeX, sizeY;\r
6206   char *str;\r
6207   RECT rect;\r
6208   MINMAXINFO *mmi;\r
6209 \r
6210   switch (message) {\r
6211   case WM_INITDIALOG: /* message: initialize dialog box */\r
6212     /* Initialize the dialog items */\r
6213     Translate(hDlg, DLG_EditComment);\r
6214     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6215     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6216     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6217     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6218     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6219     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6220     SetWindowText(hDlg, commentTitle);\r
6221     if (editComment) {\r
6222       SetFocus(hwndText);\r
6223     } else {\r
6224       SetFocus(GetDlgItem(hDlg, IDOK));\r
6225     }\r
6226     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6227                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6228                 MAKELPARAM(FALSE, 0));\r
6229     /* Size and position the dialog */\r
6230     if (!commentDialog) {\r
6231       commentDialog = hDlg;\r
6232       flags = SWP_NOZORDER;\r
6233       GetClientRect(hDlg, &rect);\r
6234       sizeX = rect.right;\r
6235       sizeY = rect.bottom;\r
6236       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6237           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6238         WINDOWPLACEMENT wp;\r
6239         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6240         wp.length = sizeof(WINDOWPLACEMENT);\r
6241         wp.flags = 0;\r
6242         wp.showCmd = SW_SHOW;\r
6243         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6244         wp.rcNormalPosition.left = wpComment.x;\r
6245         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6246         wp.rcNormalPosition.top = wpComment.y;\r
6247         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6248         SetWindowPlacement(hDlg, &wp);\r
6249 \r
6250         GetClientRect(hDlg, &rect);\r
6251         newSizeX = rect.right;\r
6252         newSizeY = rect.bottom;\r
6253         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6254                               newSizeX, newSizeY);\r
6255         sizeX = newSizeX;\r
6256         sizeY = newSizeY;\r
6257       }\r
6258     }\r
6259     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6260     return FALSE;\r
6261 \r
6262   case WM_COMMAND: /* message: received a command */\r
6263     switch (LOWORD(wParam)) {\r
6264     case IDOK:\r
6265       if (editComment) {\r
6266         char *p, *q;\r
6267         /* Read changed options from the dialog box */\r
6268         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6269         len = GetWindowTextLength(hwndText);\r
6270         str = (char *) malloc(len + 1);\r
6271         GetWindowText(hwndText, str, len + 1);\r
6272         p = q = str;\r
6273         while (*q) {\r
6274           if (*q == '\r')\r
6275             q++;\r
6276           else\r
6277             *p++ = *q++;\r
6278         }\r
6279         *p = NULLCHAR;\r
6280         ReplaceComment(commentIndex, str);\r
6281         free(str);\r
6282       }\r
6283       CommentPopDown();\r
6284       return TRUE;\r
6285 \r
6286     case IDCANCEL:\r
6287     case OPT_CancelComment:\r
6288       CommentPopDown();\r
6289       return TRUE;\r
6290 \r
6291     case OPT_ClearComment:\r
6292       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6293       break;\r
6294 \r
6295     case OPT_EditComment:\r
6296       EditCommentEvent();\r
6297       return TRUE;\r
6298 \r
6299     default:\r
6300       break;\r
6301     }\r
6302     break;\r
6303 \r
6304   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6305         if( wParam == OPT_CommentText ) {\r
6306             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6307 \r
6308             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6309                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6310                 POINTL pt;\r
6311                 LRESULT index;\r
6312 \r
6313                 pt.x = LOWORD( lpMF->lParam );\r
6314                 pt.y = HIWORD( lpMF->lParam );\r
6315 \r
6316                 if(lpMF->msg == WM_CHAR) {\r
6317                         CHARRANGE sel;\r
6318                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6319                         index = sel.cpMin;\r
6320                 } else\r
6321                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6322 \r
6323                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6324                 len = GetWindowTextLength(hwndText);\r
6325                 str = (char *) malloc(len + 1);\r
6326                 GetWindowText(hwndText, str, len + 1);\r
6327                 ReplaceComment(commentIndex, str);\r
6328                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6329                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6330                 free(str);\r
6331 \r
6332                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6333                 lpMF->msg = WM_USER;\r
6334 \r
6335                 return TRUE;\r
6336             }\r
6337         }\r
6338         break;\r
6339 \r
6340   case WM_SIZE:\r
6341     newSizeX = LOWORD(lParam);\r
6342     newSizeY = HIWORD(lParam);\r
6343     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6344     sizeX = newSizeX;\r
6345     sizeY = newSizeY;\r
6346     break;\r
6347 \r
6348   case WM_GETMINMAXINFO:\r
6349     /* Prevent resizing window too small */\r
6350     mmi = (MINMAXINFO *) lParam;\r
6351     mmi->ptMinTrackSize.x = 100;\r
6352     mmi->ptMinTrackSize.y = 100;\r
6353     break;\r
6354   }\r
6355   return FALSE;\r
6356 }\r
6357 \r
6358 VOID\r
6359 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6360 {\r
6361   FARPROC lpProc;\r
6362   char *p, *q;\r
6363 \r
6364   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6365 \r
6366   if (str == NULL) str = "";\r
6367   p = (char *) malloc(2 * strlen(str) + 2);\r
6368   q = p;\r
6369   while (*str) {\r
6370     if (*str == '\n') *q++ = '\r';\r
6371     *q++ = *str++;\r
6372   }\r
6373   *q = NULLCHAR;\r
6374   if (commentText != NULL) free(commentText);\r
6375 \r
6376   commentIndex = index;\r
6377   commentTitle = title;\r
6378   commentText = p;\r
6379   editComment = edit;\r
6380 \r
6381   if (commentDialog) {\r
6382     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6383     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6384   } else {\r
6385     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6386     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6387                  hwndMain, (DLGPROC)lpProc);\r
6388     FreeProcInstance(lpProc);\r
6389   }\r
6390   commentUp = TRUE;\r
6391 }\r
6392 \r
6393 \r
6394 /*---------------------------------------------------------------------------*\\r
6395  *\r
6396  * Type-in move dialog functions\r
6397  * \r
6398 \*---------------------------------------------------------------------------*/\r
6399 \r
6400 LRESULT CALLBACK\r
6401 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6402 {\r
6403   char move[MSG_SIZ];\r
6404   HWND hInput;\r
6405   ChessMove moveType;\r
6406   int fromX, fromY, toX, toY;\r
6407   char promoChar;\r
6408 \r
6409   switch (message) {\r
6410   case WM_INITDIALOG:\r
6411     move[0] = (char) lParam;\r
6412     move[1] = NULLCHAR;\r
6413     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6414     Translate(hDlg, DLG_TypeInMove);\r
6415     hInput = GetDlgItem(hDlg, OPT_Move);\r
6416     SetWindowText(hInput, move);\r
6417     SetFocus(hInput);\r
6418     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6419     return FALSE;\r
6420 \r
6421   case WM_COMMAND:\r
6422     switch (LOWORD(wParam)) {\r
6423     case IDOK:
6424 \r
6425       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6426       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6427       { int n; Board board;\r
6428         // [HGM] FENedit\r
6429         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6430                 EditPositionPasteFEN(move);\r
6431                 EndDialog(hDlg, TRUE);\r
6432                 return TRUE;\r
6433         }\r
6434         // [HGM] movenum: allow move number to be typed in any mode\r
6435         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6436           ToNrEvent(2*n-1);\r
6437           EndDialog(hDlg, TRUE);\r
6438           return TRUE;\r
6439         }\r
6440       }\r
6441       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6442         gameMode != Training) {\r
6443         DisplayMoveError(_("Displayed move is not current"));\r
6444       } else {\r
6445 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6446         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6447           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6448         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6449         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6450           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6451           if (gameMode != Training)\r
6452               forwardMostMove = currentMove;\r
6453           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6454         } else {\r
6455           DisplayMoveError(_("Could not parse move"));\r
6456         }\r
6457       }\r
6458       EndDialog(hDlg, TRUE);\r
6459       return TRUE;\r
6460     case IDCANCEL:\r
6461       EndDialog(hDlg, FALSE);\r
6462       return TRUE;\r
6463     default:\r
6464       break;\r
6465     }\r
6466     break;\r
6467   }\r
6468   return FALSE;\r
6469 }\r
6470 \r
6471 VOID\r
6472 PopUpMoveDialog(char firstchar)\r
6473 {\r
6474     FARPROC lpProc;\r
6475     \r
6476     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6477         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6478         gameMode == AnalyzeMode || gameMode == EditGame || \r
6479         gameMode == EditPosition || gameMode == IcsExamining ||\r
6480         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6481         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6482                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6483                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6484         gameMode == Training) {\r
6485       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6486       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6487         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6488       FreeProcInstance(lpProc);\r
6489     }\r
6490 }\r
6491 \r
6492 /*---------------------------------------------------------------------------*\\r
6493  *\r
6494  * Type-in name dialog functions\r
6495  * \r
6496 \*---------------------------------------------------------------------------*/\r
6497 \r
6498 LRESULT CALLBACK\r
6499 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6500 {\r
6501   char move[MSG_SIZ];\r
6502   HWND hInput;\r
6503 \r
6504   switch (message) {\r
6505   case WM_INITDIALOG:\r
6506     move[0] = (char) lParam;\r
6507     move[1] = NULLCHAR;\r
6508     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6509     Translate(hDlg, DLG_TypeInName);\r
6510     hInput = GetDlgItem(hDlg, OPT_Name);\r
6511     SetWindowText(hInput, move);\r
6512     SetFocus(hInput);\r
6513     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6514     return FALSE;\r
6515 \r
6516   case WM_COMMAND:\r
6517     switch (LOWORD(wParam)) {\r
6518     case IDOK:\r
6519       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6520       appData.userName = strdup(move);\r
6521       SetUserLogo();\r
6522       SetGameInfo();\r
6523       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6524         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6525         DisplayTitle(move);\r
6526       }\r
6527 \r
6528 \r
6529       EndDialog(hDlg, TRUE);\r
6530       return TRUE;\r
6531     case IDCANCEL:\r
6532       EndDialog(hDlg, FALSE);\r
6533       return TRUE;\r
6534     default:\r
6535       break;\r
6536     }\r
6537     break;\r
6538   }\r
6539   return FALSE;\r
6540 }\r
6541 \r
6542 VOID\r
6543 PopUpNameDialog(char firstchar)\r
6544 {\r
6545     FARPROC lpProc;\r
6546     \r
6547       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6548       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6549         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6550       FreeProcInstance(lpProc);\r
6551 }\r
6552 \r
6553 /*---------------------------------------------------------------------------*\\r
6554  *\r
6555  *  Error dialogs\r
6556  * \r
6557 \*---------------------------------------------------------------------------*/\r
6558 \r
6559 /* Nonmodal error box */\r
6560 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6561                              WPARAM wParam, LPARAM lParam);\r
6562 \r
6563 VOID\r
6564 ErrorPopUp(char *title, char *content)\r
6565 {\r
6566   FARPROC lpProc;\r
6567   char *p, *q;\r
6568   BOOLEAN modal = hwndMain == NULL;\r
6569 \r
6570   p = content;\r
6571   q = errorMessage;\r
6572   while (*p) {\r
6573     if (*p == '\n') {\r
6574       if (modal) {\r
6575         *q++ = ' ';\r
6576         p++;\r
6577       } else {\r
6578         *q++ = '\r';\r
6579         *q++ = *p++;\r
6580       }\r
6581     } else {\r
6582       *q++ = *p++;\r
6583     }\r
6584   }\r
6585   *q = NULLCHAR;\r
6586   strncpy(errorTitle, title, sizeof(errorTitle));\r
6587   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6588   \r
6589   if (modal) {\r
6590     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6591   } else {\r
6592     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6593     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6594                  hwndMain, (DLGPROC)lpProc);\r
6595     FreeProcInstance(lpProc);\r
6596   }\r
6597 }\r
6598 \r
6599 VOID\r
6600 ErrorPopDown()\r
6601 {\r
6602   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6603   if (errorDialog == NULL) return;\r
6604   DestroyWindow(errorDialog);\r
6605   errorDialog = NULL;\r
6606   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6607 }\r
6608 \r
6609 LRESULT CALLBACK\r
6610 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6611 {\r
6612   HANDLE hwndText;\r
6613   RECT rChild;\r
6614 \r
6615   switch (message) {\r
6616   case WM_INITDIALOG:\r
6617     GetWindowRect(hDlg, &rChild);\r
6618 \r
6619     /*\r
6620     SetWindowPos(hDlg, NULL, rChild.left,\r
6621       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6622       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6623     */\r
6624 \r
6625     /* \r
6626         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6627         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6628         and it doesn't work when you resize the dialog.\r
6629         For now, just give it a default position.\r
6630     */\r
6631     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6632     Translate(hDlg, DLG_Error);\r
6633 \r
6634     errorDialog = hDlg;\r
6635     SetWindowText(hDlg, errorTitle);\r
6636     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6637     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6638     return FALSE;\r
6639 \r
6640   case WM_COMMAND:\r
6641     switch (LOWORD(wParam)) {\r
6642     case IDOK:\r
6643     case IDCANCEL:\r
6644       if (errorDialog == hDlg) errorDialog = NULL;\r
6645       DestroyWindow(hDlg);\r
6646       return TRUE;\r
6647 \r
6648     default:\r
6649       break;\r
6650     }\r
6651     break;\r
6652   }\r
6653   return FALSE;\r
6654 }\r
6655 \r
6656 #ifdef GOTHIC\r
6657 HWND gothicDialog = NULL;\r
6658 \r
6659 LRESULT CALLBACK\r
6660 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6661 {\r
6662   HANDLE hwndText;\r
6663   RECT rChild;\r
6664   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6665 \r
6666   switch (message) {\r
6667   case WM_INITDIALOG:\r
6668     GetWindowRect(hDlg, &rChild);\r
6669 \r
6670     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6671                                                              SWP_NOZORDER);\r
6672 \r
6673     /* \r
6674         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6675         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6676         and it doesn't work when you resize the dialog.\r
6677         For now, just give it a default position.\r
6678     */\r
6679     gothicDialog = hDlg;\r
6680     SetWindowText(hDlg, errorTitle);\r
6681     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6682     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6683     return FALSE;\r
6684 \r
6685   case WM_COMMAND:\r
6686     switch (LOWORD(wParam)) {\r
6687     case IDOK:\r
6688     case IDCANCEL:\r
6689       if (errorDialog == hDlg) errorDialog = NULL;\r
6690       DestroyWindow(hDlg);\r
6691       return TRUE;\r
6692 \r
6693     default:\r
6694       break;\r
6695     }\r
6696     break;\r
6697   }\r
6698   return FALSE;\r
6699 }\r
6700 \r
6701 VOID\r
6702 GothicPopUp(char *title, VariantClass variant)\r
6703 {\r
6704   FARPROC lpProc;\r
6705   static char *lastTitle;\r
6706 \r
6707   strncpy(errorTitle, title, sizeof(errorTitle));\r
6708   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6709 \r
6710   if(lastTitle != title && gothicDialog != NULL) {\r
6711     DestroyWindow(gothicDialog);\r
6712     gothicDialog = NULL;\r
6713   }\r
6714   if(variant != VariantNormal && gothicDialog == NULL) {\r
6715     title = lastTitle;\r
6716     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6717     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6718                  hwndMain, (DLGPROC)lpProc);\r
6719     FreeProcInstance(lpProc);\r
6720   }\r
6721 }\r
6722 #endif\r
6723 \r
6724 /*---------------------------------------------------------------------------*\\r
6725  *\r
6726  *  Ics Interaction console functions\r
6727  *\r
6728 \*---------------------------------------------------------------------------*/\r
6729 \r
6730 #define HISTORY_SIZE 64\r
6731 static char *history[HISTORY_SIZE];\r
6732 int histIn = 0, histP = 0;\r
6733 \r
6734 VOID\r
6735 SaveInHistory(char *cmd)\r
6736 {\r
6737   if (history[histIn] != NULL) {\r
6738     free(history[histIn]);\r
6739     history[histIn] = NULL;\r
6740   }\r
6741   if (*cmd == NULLCHAR) return;\r
6742   history[histIn] = StrSave(cmd);\r
6743   histIn = (histIn + 1) % HISTORY_SIZE;\r
6744   if (history[histIn] != NULL) {\r
6745     free(history[histIn]);\r
6746     history[histIn] = NULL;\r
6747   }\r
6748   histP = histIn;\r
6749 }\r
6750 \r
6751 char *\r
6752 PrevInHistory(char *cmd)\r
6753 {\r
6754   int newhp;\r
6755   if (histP == histIn) {\r
6756     if (history[histIn] != NULL) free(history[histIn]);\r
6757     history[histIn] = StrSave(cmd);\r
6758   }\r
6759   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6760   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6761   histP = newhp;\r
6762   return history[histP];\r
6763 }\r
6764 \r
6765 char *\r
6766 NextInHistory()\r
6767 {\r
6768   if (histP == histIn) return NULL;\r
6769   histP = (histP + 1) % HISTORY_SIZE;\r
6770   return history[histP];   \r
6771 }\r
6772 \r
6773 HMENU\r
6774 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6775 {\r
6776   HMENU hmenu, h;\r
6777   int i = 0;\r
6778   hmenu = LoadMenu(hInst, "TextMenu");\r
6779   h = GetSubMenu(hmenu, 0);\r
6780   while (e->item) {\r
6781     if (strcmp(e->item, "-") == 0) {\r
6782       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6783     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6784       int flags = MF_STRING, j = 0;\r
6785       if (e->item[0] == '|') {\r
6786         flags |= MF_MENUBARBREAK;\r
6787         j++;\r
6788       }\r
6789       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6790       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6791     }\r
6792     e++;\r
6793     i++;\r
6794   } \r
6795   return hmenu;\r
6796 }\r
6797 \r
6798 WNDPROC consoleTextWindowProc;\r
6799 \r
6800 void\r
6801 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6802 {\r
6803   char buf[MSG_SIZ], name[MSG_SIZ];\r
6804   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6805   CHARRANGE sel;\r
6806 \r
6807   if (!getname) {\r
6808     SetWindowText(hInput, command);\r
6809     if (immediate) {\r
6810       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6811     } else {\r
6812       sel.cpMin = 999999;\r
6813       sel.cpMax = 999999;\r
6814       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6815       SetFocus(hInput);\r
6816     }\r
6817     return;\r
6818   }    \r
6819   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6820   if (sel.cpMin == sel.cpMax) {\r
6821     /* Expand to surrounding word */\r
6822     TEXTRANGE tr;\r
6823     do {\r
6824       tr.chrg.cpMax = sel.cpMin;\r
6825       tr.chrg.cpMin = --sel.cpMin;\r
6826       if (sel.cpMin < 0) break;\r
6827       tr.lpstrText = name;\r
6828       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6829     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6830     sel.cpMin++;\r
6831 \r
6832     do {\r
6833       tr.chrg.cpMin = sel.cpMax;\r
6834       tr.chrg.cpMax = ++sel.cpMax;\r
6835       tr.lpstrText = name;\r
6836       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6837     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6838     sel.cpMax--;\r
6839 \r
6840     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6841       MessageBeep(MB_ICONEXCLAMATION);\r
6842       return;\r
6843     }\r
6844     tr.chrg = sel;\r
6845     tr.lpstrText = name;\r
6846     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6847   } else {\r
6848     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6849       MessageBeep(MB_ICONEXCLAMATION);\r
6850       return;\r
6851     }\r
6852     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6853   }\r
6854   if (immediate) {\r
6855     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6856     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6857     SetWindowText(hInput, buf);\r
6858     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6859   } else {\r
6860     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6861       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6862     SetWindowText(hInput, buf);\r
6863     sel.cpMin = 999999;\r
6864     sel.cpMax = 999999;\r
6865     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6866     SetFocus(hInput);\r
6867   }\r
6868 }\r
6869 \r
6870 LRESULT CALLBACK \r
6871 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6872 {\r
6873   HWND hInput;\r
6874   CHARRANGE sel;\r
6875 \r
6876   switch (message) {\r
6877   case WM_KEYDOWN:\r
6878     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6879     if(wParam=='R') return 0;\r
6880     switch (wParam) {\r
6881     case VK_PRIOR:\r
6882       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6883       return 0;\r
6884     case VK_NEXT:\r
6885       sel.cpMin = 999999;\r
6886       sel.cpMax = 999999;\r
6887       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6888       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6889       return 0;\r
6890     }\r
6891     break;\r
6892   case WM_CHAR:\r
6893    if(wParam != '\022') {\r
6894     if (wParam == '\t') {\r
6895       if (GetKeyState(VK_SHIFT) < 0) {\r
6896         /* shifted */\r
6897         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6898         if (buttonDesc[0].hwnd) {\r
6899           SetFocus(buttonDesc[0].hwnd);\r
6900         } else {\r
6901           SetFocus(hwndMain);\r
6902         }\r
6903       } else {\r
6904         /* unshifted */\r
6905         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6906       }\r
6907     } else {\r
6908       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6909       JAWS_DELETE( SetFocus(hInput); )\r
6910       SendMessage(hInput, message, wParam, lParam);\r
6911     }\r
6912     return 0;\r
6913    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6914    lParam = -1;\r
6915   case WM_RBUTTONDOWN:\r
6916     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6917       /* Move selection here if it was empty */\r
6918       POINT pt;\r
6919       pt.x = LOWORD(lParam);\r
6920       pt.y = HIWORD(lParam);\r
6921       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6922       if (sel.cpMin == sel.cpMax) {\r
6923         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6924         sel.cpMax = sel.cpMin;\r
6925         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6926       }\r
6927       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6928 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6929       POINT pt;\r
6930       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6931       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6932       if (sel.cpMin == sel.cpMax) {\r
6933         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6934         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6935       }\r
6936       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6937         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6938       }\r
6939       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6940       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6941       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6942       MenuPopup(hwnd, pt, hmenu, -1);\r
6943 }\r
6944     }\r
6945     return 0;\r
6946   case WM_RBUTTONUP:\r
6947     if (GetKeyState(VK_SHIFT) & ~1) {\r
6948       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6949         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6950     }\r
6951     return 0;\r
6952   case WM_PASTE:\r
6953     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6954     SetFocus(hInput);\r
6955     return SendMessage(hInput, message, wParam, lParam);\r
6956   case WM_MBUTTONDOWN:\r
6957     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6958   case WM_COMMAND:\r
6959     switch (LOWORD(wParam)) {\r
6960     case IDM_QuickPaste:\r
6961       {\r
6962         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6963         if (sel.cpMin == sel.cpMax) {\r
6964           MessageBeep(MB_ICONEXCLAMATION);\r
6965           return 0;\r
6966         }\r
6967         SendMessage(hwnd, WM_COPY, 0, 0);\r
6968         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6969         SendMessage(hInput, WM_PASTE, 0, 0);\r
6970         SetFocus(hInput);\r
6971         return 0;\r
6972       }\r
6973     case IDM_Cut:\r
6974       SendMessage(hwnd, WM_CUT, 0, 0);\r
6975       return 0;\r
6976     case IDM_Paste:\r
6977       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6978       return 0;\r
6979     case IDM_Copy:\r
6980       SendMessage(hwnd, WM_COPY, 0, 0);\r
6981       return 0;\r
6982     default:\r
6983       {\r
6984         int i = LOWORD(wParam) - IDM_CommandX;\r
6985         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6986             icsTextMenuEntry[i].command != NULL) {\r
6987           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6988                    icsTextMenuEntry[i].getname,\r
6989                    icsTextMenuEntry[i].immediate);\r
6990           return 0;\r
6991         }\r
6992       }\r
6993       break;\r
6994     }\r
6995     break;\r
6996   }\r
6997   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6998 }\r
6999 \r
7000 WNDPROC consoleInputWindowProc;\r
7001 \r
7002 LRESULT CALLBACK\r
7003 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7004 {\r
7005   char buf[MSG_SIZ];\r
7006   char *p;\r
7007   static BOOL sendNextChar = FALSE;\r
7008   static BOOL quoteNextChar = FALSE;\r
7009   InputSource *is = consoleInputSource;\r
7010   CHARFORMAT cf;\r
7011   CHARRANGE sel;\r
7012 \r
7013   switch (message) {\r
7014   case WM_CHAR:\r
7015     if (!appData.localLineEditing || sendNextChar) {\r
7016       is->buf[0] = (CHAR) wParam;\r
7017       is->count = 1;\r
7018       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7019       sendNextChar = FALSE;\r
7020       return 0;\r
7021     }\r
7022     if (quoteNextChar) {\r
7023       buf[0] = (char) wParam;\r
7024       buf[1] = NULLCHAR;\r
7025       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7026       quoteNextChar = FALSE;\r
7027       return 0;\r
7028     }\r
7029     switch (wParam) {\r
7030     case '\r':   /* Enter key */\r
7031       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7032       if (consoleEcho) SaveInHistory(is->buf);\r
7033       is->buf[is->count++] = '\n';\r
7034       is->buf[is->count] = NULLCHAR;\r
7035       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7036       if (consoleEcho) {\r
7037         ConsoleOutput(is->buf, is->count, TRUE);\r
7038       } else if (appData.localLineEditing) {\r
7039         ConsoleOutput("\n", 1, TRUE);\r
7040       }\r
7041       /* fall thru */\r
7042     case '\033': /* Escape key */\r
7043       SetWindowText(hwnd, "");\r
7044       cf.cbSize = sizeof(CHARFORMAT);\r
7045       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7046       if (consoleEcho) {\r
7047         cf.crTextColor = textAttribs[ColorNormal].color;\r
7048       } else {\r
7049         cf.crTextColor = COLOR_ECHOOFF;\r
7050       }\r
7051       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7052       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7053       return 0;\r
7054     case '\t':   /* Tab key */\r
7055       if (GetKeyState(VK_SHIFT) < 0) {\r
7056         /* shifted */\r
7057         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7058       } else {\r
7059         /* unshifted */\r
7060         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7061         if (buttonDesc[0].hwnd) {\r
7062           SetFocus(buttonDesc[0].hwnd);\r
7063         } else {\r
7064           SetFocus(hwndMain);\r
7065         }\r
7066       }\r
7067       return 0;\r
7068     case '\023': /* Ctrl+S */\r
7069       sendNextChar = TRUE;\r
7070       return 0;\r
7071     case '\021': /* Ctrl+Q */\r
7072       quoteNextChar = TRUE;\r
7073       return 0;\r
7074     JAWS_REPLAY\r
7075     default:\r
7076       break;\r
7077     }\r
7078     break;\r
7079   case WM_KEYDOWN:\r
7080     switch (wParam) {\r
7081     case VK_UP:\r
7082       GetWindowText(hwnd, buf, MSG_SIZ);\r
7083       p = PrevInHistory(buf);\r
7084       if (p != NULL) {\r
7085         SetWindowText(hwnd, p);\r
7086         sel.cpMin = 999999;\r
7087         sel.cpMax = 999999;\r
7088         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7089         return 0;\r
7090       }\r
7091       break;\r
7092     case VK_DOWN:\r
7093       p = NextInHistory();\r
7094       if (p != NULL) {\r
7095         SetWindowText(hwnd, p);\r
7096         sel.cpMin = 999999;\r
7097         sel.cpMax = 999999;\r
7098         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7099         return 0;\r
7100       }\r
7101       break;\r
7102     case VK_HOME:\r
7103     case VK_END:\r
7104       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7105       /* fall thru */\r
7106     case VK_PRIOR:\r
7107     case VK_NEXT:\r
7108       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7109       return 0;\r
7110     }\r
7111     break;\r
7112   case WM_MBUTTONDOWN:\r
7113     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7114       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7115     break;\r
7116   case WM_RBUTTONUP:\r
7117     if (GetKeyState(VK_SHIFT) & ~1) {\r
7118       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7119         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7120     } else {\r
7121       POINT pt;\r
7122       HMENU hmenu;\r
7123       hmenu = LoadMenu(hInst, "InputMenu");\r
7124       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7125       if (sel.cpMin == sel.cpMax) {\r
7126         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7127         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7128       }\r
7129       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7130         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7131       }\r
7132       pt.x = LOWORD(lParam);\r
7133       pt.y = HIWORD(lParam);\r
7134       MenuPopup(hwnd, pt, hmenu, -1);\r
7135     }\r
7136     return 0;\r
7137   case WM_COMMAND:\r
7138     switch (LOWORD(wParam)) { \r
7139     case IDM_Undo:\r
7140       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7141       return 0;\r
7142     case IDM_SelectAll:\r
7143       sel.cpMin = 0;\r
7144       sel.cpMax = -1; /*999999?*/\r
7145       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7146       return 0;\r
7147     case IDM_Cut:\r
7148       SendMessage(hwnd, WM_CUT, 0, 0);\r
7149       return 0;\r
7150     case IDM_Paste:\r
7151       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7152       return 0;\r
7153     case IDM_Copy:\r
7154       SendMessage(hwnd, WM_COPY, 0, 0);\r
7155       return 0;\r
7156     }\r
7157     break;\r
7158   }\r
7159   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7160 }\r
7161 \r
7162 #define CO_MAX  100000\r
7163 #define CO_TRIM   1000\r
7164 \r
7165 LRESULT CALLBACK\r
7166 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7167 {\r
7168   static SnapData sd;\r
7169   HWND hText, hInput;\r
7170   RECT rect;\r
7171   static int sizeX, sizeY;\r
7172   int newSizeX, newSizeY;\r
7173   MINMAXINFO *mmi;\r
7174   WORD wMask;\r
7175 \r
7176   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7177   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7178 \r
7179   switch (message) {\r
7180   case WM_NOTIFY:\r
7181     if (((NMHDR*)lParam)->code == EN_LINK)\r
7182     {\r
7183       ENLINK *pLink = (ENLINK*)lParam;\r
7184       if (pLink->msg == WM_LBUTTONUP)\r
7185       {\r
7186         TEXTRANGE tr;\r
7187 \r
7188         tr.chrg = pLink->chrg;\r
7189         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7190         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7191         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7192         free(tr.lpstrText);\r
7193       }\r
7194     }\r
7195     break;\r
7196   case WM_INITDIALOG: /* message: initialize dialog box */\r
7197     hwndConsole = hDlg;\r
7198     SetFocus(hInput);\r
7199     consoleTextWindowProc = (WNDPROC)\r
7200       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7201     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7202     consoleInputWindowProc = (WNDPROC)\r
7203       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7204     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7205     Colorize(ColorNormal, TRUE);\r
7206     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7207     ChangedConsoleFont();\r
7208     GetClientRect(hDlg, &rect);\r
7209     sizeX = rect.right;\r
7210     sizeY = rect.bottom;\r
7211     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7212         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7213       WINDOWPLACEMENT wp;\r
7214       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7215       wp.length = sizeof(WINDOWPLACEMENT);\r
7216       wp.flags = 0;\r
7217       wp.showCmd = SW_SHOW;\r
7218       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7219       wp.rcNormalPosition.left = wpConsole.x;\r
7220       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7221       wp.rcNormalPosition.top = wpConsole.y;\r
7222       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7223       SetWindowPlacement(hDlg, &wp);\r
7224     }\r
7225 \r
7226    // [HGM] Chessknight's change 2004-07-13\r
7227    else { /* Determine Defaults */\r
7228        WINDOWPLACEMENT wp;\r
7229        wpConsole.x = wpMain.width + 1;\r
7230        wpConsole.y = wpMain.y;\r
7231        wpConsole.width = screenWidth -  wpMain.width;\r
7232        wpConsole.height = wpMain.height;\r
7233        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7234        wp.length = sizeof(WINDOWPLACEMENT);\r
7235        wp.flags = 0;\r
7236        wp.showCmd = SW_SHOW;\r
7237        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7238        wp.rcNormalPosition.left = wpConsole.x;\r
7239        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7240        wp.rcNormalPosition.top = wpConsole.y;\r
7241        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7242        SetWindowPlacement(hDlg, &wp);\r
7243     }\r
7244 \r
7245    // Allow hText to highlight URLs and send notifications on them\r
7246    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7247    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7248    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7249    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7250 \r
7251     return FALSE;\r
7252 \r
7253   case WM_SETFOCUS:\r
7254     SetFocus(hInput);\r
7255     return 0;\r
7256 \r
7257   case WM_CLOSE:\r
7258     ExitEvent(0);\r
7259     /* not reached */\r
7260     break;\r
7261 \r
7262   case WM_SIZE:\r
7263     if (IsIconic(hDlg)) break;\r
7264     newSizeX = LOWORD(lParam);\r
7265     newSizeY = HIWORD(lParam);\r
7266     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7267       RECT rectText, rectInput;\r
7268       POINT pt;\r
7269       int newTextHeight, newTextWidth;\r
7270       GetWindowRect(hText, &rectText);\r
7271       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7272       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7273       if (newTextHeight < 0) {\r
7274         newSizeY += -newTextHeight;\r
7275         newTextHeight = 0;\r
7276       }\r
7277       SetWindowPos(hText, NULL, 0, 0,\r
7278         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7279       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7280       pt.x = rectInput.left;\r
7281       pt.y = rectInput.top + newSizeY - sizeY;\r
7282       ScreenToClient(hDlg, &pt);\r
7283       SetWindowPos(hInput, NULL, \r
7284         pt.x, pt.y, /* needs client coords */   \r
7285         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7286         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7287     }\r
7288     sizeX = newSizeX;\r
7289     sizeY = newSizeY;\r
7290     break;\r
7291 \r
7292   case WM_GETMINMAXINFO:\r
7293     /* Prevent resizing window too small */\r
7294     mmi = (MINMAXINFO *) lParam;\r
7295     mmi->ptMinTrackSize.x = 100;\r
7296     mmi->ptMinTrackSize.y = 100;\r
7297     break;\r
7298 \r
7299   /* [AS] Snapping */\r
7300   case WM_ENTERSIZEMOVE:\r
7301     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7302 \r
7303   case WM_SIZING:\r
7304     return OnSizing( &sd, hDlg, wParam, lParam );\r
7305 \r
7306   case WM_MOVING:\r
7307     return OnMoving( &sd, hDlg, wParam, lParam );\r
7308 \r
7309   case WM_EXITSIZEMOVE:\r
7310         UpdateICSWidth(hText);\r
7311     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7312   }\r
7313 \r
7314   return DefWindowProc(hDlg, message, wParam, lParam);\r
7315 }\r
7316 \r
7317 \r
7318 VOID\r
7319 ConsoleCreate()\r
7320 {\r
7321   HWND hCons;\r
7322   if (hwndConsole) return;\r
7323   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7324   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7325 }\r
7326 \r
7327 \r
7328 VOID\r
7329 ConsoleOutput(char* data, int length, int forceVisible)\r
7330 {\r
7331   HWND hText;\r
7332   int trim, exlen;\r
7333   char *p, *q;\r
7334   char buf[CO_MAX+1];\r
7335   POINT pEnd;\r
7336   RECT rect;\r
7337   static int delayLF = 0;\r
7338   CHARRANGE savesel, sel;\r
7339 \r
7340   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7341   p = data;\r
7342   q = buf;\r
7343   if (delayLF) {\r
7344     *q++ = '\r';\r
7345     *q++ = '\n';\r
7346     delayLF = 0;\r
7347   }\r
7348   while (length--) {\r
7349     if (*p == '\n') {\r
7350       if (*++p) {\r
7351         *q++ = '\r';\r
7352         *q++ = '\n';\r
7353       } else {\r
7354         delayLF = 1;\r
7355       }\r
7356     } else if (*p == '\007') {\r
7357        MyPlaySound(&sounds[(int)SoundBell]);\r
7358        p++;\r
7359     } else {\r
7360       *q++ = *p++;\r
7361     }\r
7362   }\r
7363   *q = NULLCHAR;\r
7364   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7365   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7366   /* Save current selection */\r
7367   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7368   exlen = GetWindowTextLength(hText);\r
7369   /* Find out whether current end of text is visible */\r
7370   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7371   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7372   /* Trim existing text if it's too long */\r
7373   if (exlen + (q - buf) > CO_MAX) {\r
7374     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7375     sel.cpMin = 0;\r
7376     sel.cpMax = trim;\r
7377     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7378     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7379     exlen -= trim;\r
7380     savesel.cpMin -= trim;\r
7381     savesel.cpMax -= trim;\r
7382     if (exlen < 0) exlen = 0;\r
7383     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7384     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7385   }\r
7386   /* Append the new text */\r
7387   sel.cpMin = exlen;\r
7388   sel.cpMax = exlen;\r
7389   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7390   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7391   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7392   if (forceVisible || exlen == 0 ||\r
7393       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7394        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7395     /* Scroll to make new end of text visible if old end of text\r
7396        was visible or new text is an echo of user typein */\r
7397     sel.cpMin = 9999999;\r
7398     sel.cpMax = 9999999;\r
7399     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7400     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7401     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7402     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7403   }\r
7404   if (savesel.cpMax == exlen || forceVisible) {\r
7405     /* Move insert point to new end of text if it was at the old\r
7406        end of text or if the new text is an echo of user typein */\r
7407     sel.cpMin = 9999999;\r
7408     sel.cpMax = 9999999;\r
7409     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7410   } else {\r
7411     /* Restore previous selection */\r
7412     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7413   }\r
7414   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7415 }\r
7416 \r
7417 /*---------*/\r
7418 \r
7419 \r
7420 void\r
7421 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7422 {\r
7423   char buf[100];\r
7424   char *str;\r
7425   COLORREF oldFg, oldBg;\r
7426   HFONT oldFont;\r
7427   RECT rect;\r
7428 \r
7429   if(copyNumber > 1)
7430     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7431 \r
7432   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7433   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7434   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7435 \r
7436   rect.left = x;\r
7437   rect.right = x + squareSize;\r
7438   rect.top  = y;\r
7439   rect.bottom = y + squareSize;\r
7440   str = buf;\r
7441 \r
7442   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7443                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7444              y, ETO_CLIPPED|ETO_OPAQUE,\r
7445              &rect, str, strlen(str), NULL);\r
7446 \r
7447   (void) SetTextColor(hdc, oldFg);\r
7448   (void) SetBkColor(hdc, oldBg);\r
7449   (void) SelectObject(hdc, oldFont);\r
7450 }\r
7451 \r
7452 void\r
7453 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7454               RECT *rect, char *color, char *flagFell)\r
7455 {\r
7456   char buf[100];\r
7457   char *str;\r
7458   COLORREF oldFg, oldBg;\r
7459   HFONT oldFont;\r
7460 \r
7461   if (appData.clockMode) {\r
7462     if (tinyLayout)\r
7463       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7464     else\r
7465       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7466     str = buf;\r
7467   } else {\r
7468     str = color;\r
7469   }\r
7470 \r
7471   if (highlight) {\r
7472     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7473     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7474   } else {\r
7475     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7476     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7477   }\r
7478   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7479 \r
7480   JAWS_SILENCE\r
7481 \r
7482   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7483              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7484              rect, str, strlen(str), NULL);\r
7485   if(logoHeight > 0 && appData.clockMode) {\r
7486       RECT r;\r
7487       str += strlen(color)+2;\r
7488       r.top = rect->top + logoHeight/2;\r
7489       r.left = rect->left;\r
7490       r.right = rect->right;\r
7491       r.bottom = rect->bottom;\r
7492       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7493                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7494                  &r, str, strlen(str), NULL);\r
7495   }\r
7496   (void) SetTextColor(hdc, oldFg);\r
7497   (void) SetBkColor(hdc, oldBg);\r
7498   (void) SelectObject(hdc, oldFont);\r
7499 }\r
7500 \r
7501 \r
7502 int\r
7503 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7504            OVERLAPPED *ovl)\r
7505 {\r
7506   int ok, err;\r
7507 \r
7508   /* [AS]  */\r
7509   if( count <= 0 ) {\r
7510     if (appData.debugMode) {\r
7511       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7512     }\r
7513 \r
7514     return ERROR_INVALID_USER_BUFFER;\r
7515   }\r
7516 \r
7517   ResetEvent(ovl->hEvent);\r
7518   ovl->Offset = ovl->OffsetHigh = 0;\r
7519   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7520   if (ok) {\r
7521     err = NO_ERROR;\r
7522   } else {\r
7523     err = GetLastError();\r
7524     if (err == ERROR_IO_PENDING) {\r
7525       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7526       if (ok)\r
7527         err = NO_ERROR;\r
7528       else\r
7529         err = GetLastError();\r
7530     }\r
7531   }\r
7532   return err;\r
7533 }\r
7534 \r
7535 int\r
7536 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7537             OVERLAPPED *ovl)\r
7538 {\r
7539   int ok, err;\r
7540 \r
7541   ResetEvent(ovl->hEvent);\r
7542   ovl->Offset = ovl->OffsetHigh = 0;\r
7543   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7544   if (ok) {\r
7545     err = NO_ERROR;\r
7546   } else {\r
7547     err = GetLastError();\r
7548     if (err == ERROR_IO_PENDING) {\r
7549       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7550       if (ok)\r
7551         err = NO_ERROR;\r
7552       else\r
7553         err = GetLastError();\r
7554     }\r
7555   }\r
7556   return err;\r
7557 }\r
7558 \r
7559 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7560 void CheckForInputBufferFull( InputSource * is )\r
7561 {\r
7562     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7563         /* Look for end of line */\r
7564         char * p = is->buf;\r
7565         \r
7566         while( p < is->next && *p != '\n' ) {\r
7567             p++;\r
7568         }\r
7569 \r
7570         if( p >= is->next ) {\r
7571             if (appData.debugMode) {\r
7572                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7573             }\r
7574 \r
7575             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7576             is->count = (DWORD) -1;\r
7577             is->next = is->buf;\r
7578         }\r
7579     }\r
7580 }\r
7581 \r
7582 DWORD\r
7583 InputThread(LPVOID arg)\r
7584 {\r
7585   InputSource *is;\r
7586   OVERLAPPED ovl;\r
7587 \r
7588   is = (InputSource *) arg;\r
7589   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7590   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7591   while (is->hThread != NULL) {\r
7592     is->error = DoReadFile(is->hFile, is->next,\r
7593                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7594                            &is->count, &ovl);\r
7595     if (is->error == NO_ERROR) {\r
7596       is->next += is->count;\r
7597     } else {\r
7598       if (is->error == ERROR_BROKEN_PIPE) {\r
7599         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7600         is->count = 0;\r
7601       } else {\r
7602         is->count = (DWORD) -1;\r
7603         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7604         break; \r
7605       }\r
7606     }\r
7607 \r
7608     CheckForInputBufferFull( is );\r
7609 \r
7610     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7611 \r
7612     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7613 \r
7614     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7615   }\r
7616 \r
7617   CloseHandle(ovl.hEvent);\r
7618   CloseHandle(is->hFile);\r
7619 \r
7620   if (appData.debugMode) {\r
7621     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7622   }\r
7623 \r
7624   return 0;\r
7625 }\r
7626 \r
7627 \r
7628 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7629 DWORD\r
7630 NonOvlInputThread(LPVOID arg)\r
7631 {\r
7632   InputSource *is;\r
7633   char *p, *q;\r
7634   int i;\r
7635   char prev;\r
7636 \r
7637   is = (InputSource *) arg;\r
7638   while (is->hThread != NULL) {\r
7639     is->error = ReadFile(is->hFile, is->next,\r
7640                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7641                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7642     if (is->error == NO_ERROR) {\r
7643       /* Change CRLF to LF */\r
7644       if (is->next > is->buf) {\r
7645         p = is->next - 1;\r
7646         i = is->count + 1;\r
7647       } else {\r
7648         p = is->next;\r
7649         i = is->count;\r
7650       }\r
7651       q = p;\r
7652       prev = NULLCHAR;\r
7653       while (i > 0) {\r
7654         if (prev == '\r' && *p == '\n') {\r
7655           *(q-1) = '\n';\r
7656           is->count--;\r
7657         } else { \r
7658           *q++ = *p;\r
7659         }\r
7660         prev = *p++;\r
7661         i--;\r
7662       }\r
7663       *q = NULLCHAR;\r
7664       is->next = q;\r
7665     } else {\r
7666       if (is->error == ERROR_BROKEN_PIPE) {\r
7667         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7668         is->count = 0; \r
7669       } else {\r
7670         is->count = (DWORD) -1;\r
7671       }\r
7672     }\r
7673 \r
7674     CheckForInputBufferFull( is );\r
7675 \r
7676     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7677 \r
7678     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7679 \r
7680     if (is->count < 0) break;  /* Quit on error */\r
7681   }\r
7682   CloseHandle(is->hFile);\r
7683   return 0;\r
7684 }\r
7685 \r
7686 DWORD\r
7687 SocketInputThread(LPVOID arg)\r
7688 {\r
7689   InputSource *is;\r
7690 \r
7691   is = (InputSource *) arg;\r
7692   while (is->hThread != NULL) {\r
7693     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7694     if ((int)is->count == SOCKET_ERROR) {\r
7695       is->count = (DWORD) -1;\r
7696       is->error = WSAGetLastError();\r
7697     } else {\r
7698       is->error = NO_ERROR;\r
7699       is->next += is->count;\r
7700       if (is->count == 0 && is->second == is) {\r
7701         /* End of file on stderr; quit with no message */\r
7702         break;\r
7703       }\r
7704     }\r
7705     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7706 \r
7707     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7708 \r
7709     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7710   }\r
7711   return 0;\r
7712 }\r
7713 \r
7714 VOID\r
7715 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7716 {\r
7717   InputSource *is;\r
7718 \r
7719   is = (InputSource *) lParam;\r
7720   if (is->lineByLine) {\r
7721     /* Feed in lines one by one */\r
7722     char *p = is->buf;\r
7723     char *q = p;\r
7724     while (q < is->next) {\r
7725       if (*q++ == '\n') {\r
7726         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7727         p = q;\r
7728       }\r
7729     }\r
7730     \r
7731     /* Move any partial line to the start of the buffer */\r
7732     q = is->buf;\r
7733     while (p < is->next) {\r
7734       *q++ = *p++;\r
7735     }\r
7736     is->next = q;\r
7737 \r
7738     if (is->error != NO_ERROR || is->count == 0) {\r
7739       /* Notify backend of the error.  Note: If there was a partial\r
7740          line at the end, it is not flushed through. */\r
7741       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7742     }\r
7743   } else {\r
7744     /* Feed in the whole chunk of input at once */\r
7745     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7746     is->next = is->buf;\r
7747   }\r
7748 }\r
7749 \r
7750 /*---------------------------------------------------------------------------*\\r
7751  *\r
7752  *  Menu enables. Used when setting various modes.\r
7753  *\r
7754 \*---------------------------------------------------------------------------*/\r
7755 \r
7756 typedef struct {\r
7757   int item;\r
7758   int flags;\r
7759 } Enables;\r
7760 \r
7761 VOID\r
7762 GreyRevert(Boolean grey)\r
7763 { // [HGM] vari: for retracting variations in local mode\r
7764   HMENU hmenu = GetMenu(hwndMain);\r
7765   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7766   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7767 }\r
7768 \r
7769 VOID\r
7770 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7771 {\r
7772   while (enab->item > 0) {\r
7773     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7774     enab++;\r
7775   }\r
7776 }\r
7777 \r
7778 Enables gnuEnables[] = {\r
7779   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7792   { -1, -1 }\r
7793 };\r
7794 \r
7795 Enables icsEnables[] = {\r
7796   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7804   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7808   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7809   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7810   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7811   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7812   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7813   { -1, -1 }\r
7814 };\r
7815 \r
7816 #if ZIPPY\r
7817 Enables zippyEnables[] = {\r
7818   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7819   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7820   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7821   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7822   { -1, -1 }\r
7823 };\r
7824 #endif\r
7825 \r
7826 Enables ncpEnables[] = {\r
7827   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7836   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7837   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7848   { -1, -1 }\r
7849 };\r
7850 \r
7851 Enables trainingOnEnables[] = {\r
7852   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7861   { -1, -1 }\r
7862 };\r
7863 \r
7864 Enables trainingOffEnables[] = {\r
7865   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7868   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7870   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7871   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7872   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7874   { -1, -1 }\r
7875 };\r
7876 \r
7877 /* These modify either ncpEnables or gnuEnables */\r
7878 Enables cmailEnables[] = {\r
7879   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7881   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7882   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7884   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7886   { -1, -1 }\r
7887 };\r
7888 \r
7889 Enables machineThinkingEnables[] = {\r
7890   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7906   { -1, -1 }\r
7907 };\r
7908 \r
7909 Enables userThinkingEnables[] = {\r
7910   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7926   { -1, -1 }\r
7927 };\r
7928 \r
7929 /*---------------------------------------------------------------------------*\\r
7930  *\r
7931  *  Front-end interface functions exported by XBoard.\r
7932  *  Functions appear in same order as prototypes in frontend.h.\r
7933  * \r
7934 \*---------------------------------------------------------------------------*/\r
7935 VOID\r
7936 ModeHighlight()\r
7937 {\r
7938   static UINT prevChecked = 0;\r
7939   static int prevPausing = 0;\r
7940   UINT nowChecked;\r
7941 \r
7942   if (pausing != prevPausing) {\r
7943     prevPausing = pausing;\r
7944     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7945                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7946     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7947   }\r
7948 \r
7949   switch (gameMode) {\r
7950   case BeginningOfGame:\r
7951     if (appData.icsActive)\r
7952       nowChecked = IDM_IcsClient;\r
7953     else if (appData.noChessProgram)\r
7954       nowChecked = IDM_EditGame;\r
7955     else\r
7956       nowChecked = IDM_MachineBlack;\r
7957     break;\r
7958   case MachinePlaysBlack:\r
7959     nowChecked = IDM_MachineBlack;\r
7960     break;\r
7961   case MachinePlaysWhite:\r
7962     nowChecked = IDM_MachineWhite;\r
7963     break;\r
7964   case TwoMachinesPlay:\r
7965     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7966     break;\r
7967   case AnalyzeMode:\r
7968     nowChecked = IDM_AnalysisMode;\r
7969     break;\r
7970   case AnalyzeFile:\r
7971     nowChecked = IDM_AnalyzeFile;\r
7972     break;\r
7973   case EditGame:\r
7974     nowChecked = IDM_EditGame;\r
7975     break;\r
7976   case PlayFromGameFile:\r
7977     nowChecked = IDM_LoadGame;\r
7978     break;\r
7979   case EditPosition:\r
7980     nowChecked = IDM_EditPosition;\r
7981     break;\r
7982   case Training:\r
7983     nowChecked = IDM_Training;\r
7984     break;\r
7985   case IcsPlayingWhite:\r
7986   case IcsPlayingBlack:\r
7987   case IcsObserving:\r
7988   case IcsIdle:\r
7989     nowChecked = IDM_IcsClient;\r
7990     break;\r
7991   default:\r
7992   case EndOfGame:\r
7993     nowChecked = 0;\r
7994     break;\r
7995   }\r
7996   if (prevChecked != 0)\r
7997     (void) CheckMenuItem(GetMenu(hwndMain),\r
7998                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7999   if (nowChecked != 0)\r
8000     (void) CheckMenuItem(GetMenu(hwndMain),\r
8001                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8002 \r
8003   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8004     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8005                           MF_BYCOMMAND|MF_ENABLED);\r
8006   } else {\r
8007     (void) EnableMenuItem(GetMenu(hwndMain), \r
8008                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8009   }\r
8010 \r
8011   prevChecked = nowChecked;\r
8012 \r
8013   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8014   if (appData.icsActive) {\r
8015        if (appData.icsEngineAnalyze) {\r
8016                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8017                        MF_BYCOMMAND|MF_CHECKED);\r
8018        } else {\r
8019                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8020                        MF_BYCOMMAND|MF_UNCHECKED);\r
8021        }\r
8022   }\r
8023 }\r
8024 \r
8025 VOID\r
8026 SetICSMode()\r
8027 {\r
8028   HMENU hmenu = GetMenu(hwndMain);\r
8029   SetMenuEnables(hmenu, icsEnables);\r
8030   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8031     MF_BYPOSITION|MF_ENABLED);\r
8032 #if ZIPPY\r
8033   if (appData.zippyPlay) {\r
8034     SetMenuEnables(hmenu, zippyEnables);\r
8035     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8036          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8037           MF_BYCOMMAND|MF_ENABLED);\r
8038   }\r
8039 #endif\r
8040 }\r
8041 \r
8042 VOID\r
8043 SetGNUMode()\r
8044 {\r
8045   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8046 }\r
8047 \r
8048 VOID\r
8049 SetNCPMode()\r
8050 {\r
8051   HMENU hmenu = GetMenu(hwndMain);\r
8052   SetMenuEnables(hmenu, ncpEnables);\r
8053   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8054     MF_BYPOSITION|MF_GRAYED);\r
8055     DrawMenuBar(hwndMain);\r
8056 }\r
8057 \r
8058 VOID\r
8059 SetCmailMode()\r
8060 {\r
8061   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8062 }\r
8063 \r
8064 VOID \r
8065 SetTrainingModeOn()\r
8066 {\r
8067   int i;\r
8068   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8069   for (i = 0; i < N_BUTTONS; i++) {\r
8070     if (buttonDesc[i].hwnd != NULL)\r
8071       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8072   }\r
8073   CommentPopDown();\r
8074 }\r
8075 \r
8076 VOID SetTrainingModeOff()\r
8077 {\r
8078   int i;\r
8079   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8080   for (i = 0; i < N_BUTTONS; i++) {\r
8081     if (buttonDesc[i].hwnd != NULL)\r
8082       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8083   }\r
8084 }\r
8085 \r
8086 \r
8087 VOID\r
8088 SetUserThinkingEnables()\r
8089 {\r
8090   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8091 }\r
8092 \r
8093 VOID\r
8094 SetMachineThinkingEnables()\r
8095 {\r
8096   HMENU hMenu = GetMenu(hwndMain);\r
8097   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8098 \r
8099   SetMenuEnables(hMenu, machineThinkingEnables);\r
8100 \r
8101   if (gameMode == MachinePlaysBlack) {\r
8102     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8103   } else if (gameMode == MachinePlaysWhite) {\r
8104     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8105   } else if (gameMode == TwoMachinesPlay) {\r
8106     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8107   }\r
8108 }\r
8109 \r
8110 \r
8111 VOID\r
8112 DisplayTitle(char *str)\r
8113 {\r
8114   char title[MSG_SIZ], *host;\r
8115   if (str[0] != NULLCHAR) {\r
8116     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8117   } else if (appData.icsActive) {\r
8118     if (appData.icsCommPort[0] != NULLCHAR)\r
8119       host = "ICS";\r
8120     else \r
8121       host = appData.icsHost;\r
8122       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8123   } else if (appData.noChessProgram) {\r
8124     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8125   } else {\r
8126     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8127     strcat(title, ": ");\r
8128     strcat(title, first.tidy);\r
8129   }\r
8130   SetWindowText(hwndMain, title);\r
8131 }\r
8132 \r
8133 \r
8134 VOID\r
8135 DisplayMessage(char *str1, char *str2)\r
8136 {\r
8137   HDC hdc;\r
8138   HFONT oldFont;\r
8139   int remain = MESSAGE_TEXT_MAX - 1;\r
8140   int len;\r
8141 \r
8142   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8143   messageText[0] = NULLCHAR;\r
8144   if (*str1) {\r
8145     len = strlen(str1);\r
8146     if (len > remain) len = remain;\r
8147     strncpy(messageText, str1, len);\r
8148     messageText[len] = NULLCHAR;\r
8149     remain -= len;\r
8150   }\r
8151   if (*str2 && remain >= 2) {\r
8152     if (*str1) {\r
8153       strcat(messageText, "  ");\r
8154       remain -= 2;\r
8155     }\r
8156     len = strlen(str2);\r
8157     if (len > remain) len = remain;\r
8158     strncat(messageText, str2, len);\r
8159   }\r
8160   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8161 \r
8162   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8163 \r
8164   SAYMACHINEMOVE();\r
8165 \r
8166   hdc = GetDC(hwndMain);\r
8167   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8168   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8169              &messageRect, messageText, strlen(messageText), NULL);\r
8170   (void) SelectObject(hdc, oldFont);\r
8171   (void) ReleaseDC(hwndMain, hdc);\r
8172 }\r
8173 \r
8174 VOID\r
8175 DisplayError(char *str, int error)\r
8176 {\r
8177   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8178   int len;\r
8179 \r
8180   if (error == 0) {\r
8181     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8182   } else {\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   }\r
8198   \r
8199   ErrorPopUp(_("Error"), buf);\r
8200 }\r
8201 \r
8202 \r
8203 VOID\r
8204 DisplayMoveError(char *str)\r
8205 {\r
8206   fromX = fromY = -1;\r
8207   ClearHighlights();\r
8208   DrawPosition(FALSE, NULL);\r
8209   if (appData.popupMoveErrors) {\r
8210     ErrorPopUp(_("Error"), str);\r
8211   } else {\r
8212     DisplayMessage(str, "");\r
8213     moveErrorMessageUp = TRUE;\r
8214   }\r
8215 }\r
8216 \r
8217 VOID\r
8218 DisplayFatalError(char *str, int error, int exitStatus)\r
8219 {\r
8220   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8221   int len;\r
8222   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8223 \r
8224   if (error != 0) {\r
8225     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8226                         NULL, error, LANG_NEUTRAL,\r
8227                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8228     if (len > 0) {\r
8229       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8230     } else {\r
8231       ErrorMap *em = errmap;\r
8232       while (em->err != 0 && em->err != error) em++;\r
8233       if (em->err != 0) {\r
8234         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8235       } else {\r
8236         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8237       }\r
8238     }\r
8239     str = buf;\r
8240   }\r
8241   if (appData.debugMode) {\r
8242     fprintf(debugFP, "%s: %s\n", label, str);\r
8243   }\r
8244   if (appData.popupExitMessage) {\r
8245     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8246                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8247   }\r
8248   ExitEvent(exitStatus);\r
8249 }\r
8250 \r
8251 \r
8252 VOID\r
8253 DisplayInformation(char *str)\r
8254 {\r
8255   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8256 }\r
8257 \r
8258 \r
8259 VOID\r
8260 DisplayNote(char *str)\r
8261 {\r
8262   ErrorPopUp(_("Note"), str);\r
8263 }\r
8264 \r
8265 \r
8266 typedef struct {\r
8267   char *title, *question, *replyPrefix;\r
8268   ProcRef pr;\r
8269 } QuestionParams;\r
8270 \r
8271 LRESULT CALLBACK\r
8272 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8273 {\r
8274   static QuestionParams *qp;\r
8275   char reply[MSG_SIZ];\r
8276   int len, err;\r
8277 \r
8278   switch (message) {\r
8279   case WM_INITDIALOG:\r
8280     qp = (QuestionParams *) lParam;\r
8281     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8282     Translate(hDlg, DLG_Question);\r
8283     SetWindowText(hDlg, qp->title);\r
8284     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8285     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8286     return FALSE;\r
8287 \r
8288   case WM_COMMAND:\r
8289     switch (LOWORD(wParam)) {\r
8290     case IDOK:\r
8291       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8292       if (*reply) strcat(reply, " ");\r
8293       len = strlen(reply);\r
8294       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8295       strcat(reply, "\n");\r
8296       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8297       EndDialog(hDlg, TRUE);\r
8298       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8299       return TRUE;\r
8300     case IDCANCEL:\r
8301       EndDialog(hDlg, FALSE);\r
8302       return TRUE;\r
8303     default:\r
8304       break;\r
8305     }\r
8306     break;\r
8307   }\r
8308   return FALSE;\r
8309 }\r
8310 \r
8311 VOID\r
8312 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8313 {\r
8314     QuestionParams qp;\r
8315     FARPROC lpProc;\r
8316     \r
8317     qp.title = title;\r
8318     qp.question = question;\r
8319     qp.replyPrefix = replyPrefix;\r
8320     qp.pr = pr;\r
8321     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8322     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8323       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8324     FreeProcInstance(lpProc);\r
8325 }\r
8326 \r
8327 /* [AS] Pick FRC position */\r
8328 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8329 {\r
8330     static int * lpIndexFRC;\r
8331     BOOL index_is_ok;\r
8332     char buf[16];\r
8333 \r
8334     switch( message )\r
8335     {\r
8336     case WM_INITDIALOG:\r
8337         lpIndexFRC = (int *) lParam;\r
8338 \r
8339         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8340         Translate(hDlg, DLG_NewGameFRC);\r
8341 \r
8342         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8343         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8344         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8345         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8346 \r
8347         break;\r
8348 \r
8349     case WM_COMMAND:\r
8350         switch( LOWORD(wParam) ) {\r
8351         case IDOK:\r
8352             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8353             EndDialog( hDlg, 0 );\r
8354             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8355             return TRUE;\r
8356         case IDCANCEL:\r
8357             EndDialog( hDlg, 1 );   \r
8358             return TRUE;\r
8359         case IDC_NFG_Edit:\r
8360             if( HIWORD(wParam) == EN_CHANGE ) {\r
8361                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8362 \r
8363                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8364             }\r
8365             return TRUE;\r
8366         case IDC_NFG_Random:\r
8367           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8368             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8369             return TRUE;\r
8370         }\r
8371 \r
8372         break;\r
8373     }\r
8374 \r
8375     return FALSE;\r
8376 }\r
8377 \r
8378 int NewGameFRC()\r
8379 {\r
8380     int result;\r
8381     int index = appData.defaultFrcPosition;\r
8382     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8383 \r
8384     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8385 \r
8386     if( result == 0 ) {\r
8387         appData.defaultFrcPosition = index;\r
8388     }\r
8389 \r
8390     return result;\r
8391 }\r
8392 \r
8393 /* [AS] Game list options. Refactored by HGM */\r
8394 \r
8395 HWND gameListOptionsDialog;\r
8396 \r
8397 // low-level front-end: clear text edit / list widget\r
8398 void\r
8399 GLT_ClearList()\r
8400 {\r
8401     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8402 }\r
8403 \r
8404 // low-level front-end: clear text edit / list widget\r
8405 void\r
8406 GLT_DeSelectList()\r
8407 {\r
8408     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8409 }\r
8410 \r
8411 // low-level front-end: append line to text edit / list widget\r
8412 void\r
8413 GLT_AddToList( char *name )\r
8414 {\r
8415     if( name != 0 ) {\r
8416             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8417     }\r
8418 }\r
8419 \r
8420 // low-level front-end: get line from text edit / list widget\r
8421 Boolean\r
8422 GLT_GetFromList( int index, char *name )\r
8423 {\r
8424     if( name != 0 ) {\r
8425             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8426                 return TRUE;\r
8427     }\r
8428     return FALSE;\r
8429 }\r
8430 \r
8431 void GLT_MoveSelection( HWND hDlg, int delta )\r
8432 {\r
8433     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8434     int idx2 = idx1 + delta;\r
8435     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8436 \r
8437     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8438         char buf[128];\r
8439 \r
8440         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8441         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8442         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8443         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8444     }\r
8445 }\r
8446 \r
8447 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8448 {\r
8449     switch( message )\r
8450     {\r
8451     case WM_INITDIALOG:\r
8452         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8453         \r
8454         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8455         Translate(hDlg, DLG_GameListOptions);\r
8456 \r
8457         /* Initialize list */\r
8458         GLT_TagsToList( lpUserGLT );\r
8459 \r
8460         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8461 \r
8462         break;\r
8463 \r
8464     case WM_COMMAND:\r
8465         switch( LOWORD(wParam) ) {\r
8466         case IDOK:\r
8467             GLT_ParseList();\r
8468             EndDialog( hDlg, 0 );\r
8469             return TRUE;\r
8470         case IDCANCEL:\r
8471             EndDialog( hDlg, 1 );\r
8472             return TRUE;\r
8473 \r
8474         case IDC_GLT_Default:\r
8475             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8476             return TRUE;\r
8477 \r
8478         case IDC_GLT_Restore:\r
8479             GLT_TagsToList( appData.gameListTags );\r
8480             return TRUE;\r
8481 \r
8482         case IDC_GLT_Up:\r
8483             GLT_MoveSelection( hDlg, -1 );\r
8484             return TRUE;\r
8485 \r
8486         case IDC_GLT_Down:\r
8487             GLT_MoveSelection( hDlg, +1 );\r
8488             return TRUE;\r
8489         }\r
8490 \r
8491         break;\r
8492     }\r
8493 \r
8494     return FALSE;\r
8495 }\r
8496 \r
8497 int GameListOptions()\r
8498 {\r
8499     int result;\r
8500     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8501 \r
8502       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8503 \r
8504     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8505 \r
8506     if( result == 0 ) {\r
8507         /* [AS] Memory leak here! */\r
8508         appData.gameListTags = strdup( lpUserGLT ); \r
8509     }\r
8510 \r
8511     return result;\r
8512 }\r
8513 \r
8514 VOID\r
8515 DisplayIcsInteractionTitle(char *str)\r
8516 {\r
8517   char consoleTitle[MSG_SIZ];\r
8518 \r
8519     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8520   SetWindowText(hwndConsole, consoleTitle);\r
8521 }\r
8522 \r
8523 void\r
8524 DrawPosition(int fullRedraw, Board board)\r
8525 {\r
8526   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8527 }\r
8528 \r
8529 void NotifyFrontendLogin()\r
8530 {\r
8531         if (hwndConsole)\r
8532                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8533 }\r
8534 \r
8535 VOID\r
8536 ResetFrontEnd()\r
8537 {\r
8538   fromX = fromY = -1;\r
8539   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8540     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8541     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8542     dragInfo.lastpos = dragInfo.pos;\r
8543     dragInfo.start.x = dragInfo.start.y = -1;\r
8544     dragInfo.from = dragInfo.start;\r
8545     ReleaseCapture();\r
8546     DrawPosition(TRUE, NULL);\r
8547   }\r
8548   TagsPopDown();\r
8549 }\r
8550 \r
8551 \r
8552 VOID\r
8553 CommentPopUp(char *title, char *str)\r
8554 {\r
8555   HWND hwnd = GetActiveWindow();\r
8556   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8557   SAY(str);\r
8558   SetActiveWindow(hwnd);\r
8559 }\r
8560 \r
8561 VOID\r
8562 CommentPopDown(void)\r
8563 {\r
8564   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8565   if (commentDialog) {\r
8566     ShowWindow(commentDialog, SW_HIDE);\r
8567   }\r
8568   commentUp = FALSE;\r
8569 }\r
8570 \r
8571 VOID\r
8572 EditCommentPopUp(int index, char *title, char *str)\r
8573 {\r
8574   EitherCommentPopUp(index, title, str, TRUE);\r
8575 }\r
8576 \r
8577 \r
8578 VOID\r
8579 RingBell()\r
8580 {\r
8581   MyPlaySound(&sounds[(int)SoundMove]);\r
8582 }\r
8583 \r
8584 VOID PlayIcsWinSound()\r
8585 {\r
8586   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8587 }\r
8588 \r
8589 VOID PlayIcsLossSound()\r
8590 {\r
8591   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8592 }\r
8593 \r
8594 VOID PlayIcsDrawSound()\r
8595 {\r
8596   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8597 }\r
8598 \r
8599 VOID PlayIcsUnfinishedSound()\r
8600 {\r
8601   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8602 }\r
8603 \r
8604 VOID\r
8605 PlayAlarmSound()\r
8606 {\r
8607   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8608 }\r
8609 \r
8610 \r
8611 VOID\r
8612 EchoOn()\r
8613 {\r
8614   HWND hInput;\r
8615   consoleEcho = TRUE;\r
8616   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8617   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8618   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8619 }\r
8620 \r
8621 \r
8622 VOID\r
8623 EchoOff()\r
8624 {\r
8625   CHARFORMAT cf;\r
8626   HWND hInput;\r
8627   consoleEcho = FALSE;\r
8628   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8629   /* This works OK: set text and background both to the same color */\r
8630   cf = consoleCF;\r
8631   cf.crTextColor = COLOR_ECHOOFF;\r
8632   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8633   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8634 }\r
8635 \r
8636 /* No Raw()...? */\r
8637 \r
8638 void Colorize(ColorClass cc, int continuation)\r
8639 {\r
8640   currentColorClass = cc;\r
8641   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8642   consoleCF.crTextColor = textAttribs[cc].color;\r
8643   consoleCF.dwEffects = textAttribs[cc].effects;\r
8644   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8645 }\r
8646 \r
8647 char *\r
8648 UserName()\r
8649 {\r
8650   static char buf[MSG_SIZ];\r
8651   DWORD bufsiz = MSG_SIZ;\r
8652 \r
8653   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8654         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8655   }\r
8656   if (!GetUserName(buf, &bufsiz)) {\r
8657     /*DisplayError("Error getting user name", GetLastError());*/\r
8658     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8659   }\r
8660   return buf;\r
8661 }\r
8662 \r
8663 char *\r
8664 HostName()\r
8665 {\r
8666   static char buf[MSG_SIZ];\r
8667   DWORD bufsiz = MSG_SIZ;\r
8668 \r
8669   if (!GetComputerName(buf, &bufsiz)) {\r
8670     /*DisplayError("Error getting host name", GetLastError());*/\r
8671     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8672   }\r
8673   return buf;\r
8674 }\r
8675 \r
8676 \r
8677 int\r
8678 ClockTimerRunning()\r
8679 {\r
8680   return clockTimerEvent != 0;\r
8681 }\r
8682 \r
8683 int\r
8684 StopClockTimer()\r
8685 {\r
8686   if (clockTimerEvent == 0) return FALSE;\r
8687   KillTimer(hwndMain, clockTimerEvent);\r
8688   clockTimerEvent = 0;\r
8689   return TRUE;\r
8690 }\r
8691 \r
8692 void\r
8693 StartClockTimer(long millisec)\r
8694 {\r
8695   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8696                              (UINT) millisec, NULL);\r
8697 }\r
8698 \r
8699 void\r
8700 DisplayWhiteClock(long timeRemaining, int highlight)\r
8701 {\r
8702   HDC hdc;\r
8703   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8704 \r
8705   if(appData.noGUI) return;\r
8706   hdc = GetDC(hwndMain);\r
8707   if (!IsIconic(hwndMain)) {\r
8708     DisplayAClock(hdc, timeRemaining, highlight, \r
8709                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8710   }\r
8711   if (highlight && iconCurrent == iconBlack) {\r
8712     iconCurrent = iconWhite;\r
8713     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8714     if (IsIconic(hwndMain)) {\r
8715       DrawIcon(hdc, 2, 2, iconCurrent);\r
8716     }\r
8717   }\r
8718   (void) ReleaseDC(hwndMain, hdc);\r
8719   if (hwndConsole)\r
8720     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8721 }\r
8722 \r
8723 void\r
8724 DisplayBlackClock(long timeRemaining, int highlight)\r
8725 {\r
8726   HDC hdc;\r
8727   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8728 \r
8729   if(appData.noGUI) return;\r
8730   hdc = GetDC(hwndMain);\r
8731   if (!IsIconic(hwndMain)) {\r
8732     DisplayAClock(hdc, timeRemaining, highlight, \r
8733                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8734   }\r
8735   if (highlight && iconCurrent == iconWhite) {\r
8736     iconCurrent = iconBlack;\r
8737     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8738     if (IsIconic(hwndMain)) {\r
8739       DrawIcon(hdc, 2, 2, iconCurrent);\r
8740     }\r
8741   }\r
8742   (void) ReleaseDC(hwndMain, hdc);\r
8743   if (hwndConsole)\r
8744     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8745 }\r
8746 \r
8747 \r
8748 int\r
8749 LoadGameTimerRunning()\r
8750 {\r
8751   return loadGameTimerEvent != 0;\r
8752 }\r
8753 \r
8754 int\r
8755 StopLoadGameTimer()\r
8756 {\r
8757   if (loadGameTimerEvent == 0) return FALSE;\r
8758   KillTimer(hwndMain, loadGameTimerEvent);\r
8759   loadGameTimerEvent = 0;\r
8760   return TRUE;\r
8761 }\r
8762 \r
8763 void\r
8764 StartLoadGameTimer(long millisec)\r
8765 {\r
8766   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8767                                 (UINT) millisec, NULL);\r
8768 }\r
8769 \r
8770 void\r
8771 AutoSaveGame()\r
8772 {\r
8773   char *defName;\r
8774   FILE *f;\r
8775   char fileTitle[MSG_SIZ];\r
8776 \r
8777   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8778   f = OpenFileDialog(hwndMain, "a", defName,\r
8779                      appData.oldSaveStyle ? "gam" : "pgn",\r
8780                      GAME_FILT, \r
8781                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8782   if (f != NULL) {\r
8783     SaveGame(f, 0, "");\r
8784     fclose(f);\r
8785   }\r
8786 }\r
8787 \r
8788 \r
8789 void\r
8790 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8791 {\r
8792   if (delayedTimerEvent != 0) {\r
8793     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8794       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8795     }\r
8796     KillTimer(hwndMain, delayedTimerEvent);\r
8797     delayedTimerEvent = 0;\r
8798     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8799     delayedTimerCallback();\r
8800   }\r
8801   delayedTimerCallback = cb;\r
8802   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8803                                 (UINT) millisec, NULL);\r
8804 }\r
8805 \r
8806 DelayedEventCallback\r
8807 GetDelayedEvent()\r
8808 {\r
8809   if (delayedTimerEvent) {\r
8810     return delayedTimerCallback;\r
8811   } else {\r
8812     return NULL;\r
8813   }\r
8814 }\r
8815 \r
8816 void\r
8817 CancelDelayedEvent()\r
8818 {\r
8819   if (delayedTimerEvent) {\r
8820     KillTimer(hwndMain, delayedTimerEvent);\r
8821     delayedTimerEvent = 0;\r
8822   }\r
8823 }\r
8824 \r
8825 DWORD GetWin32Priority(int nice)\r
8826 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8827 /*\r
8828 REALTIME_PRIORITY_CLASS     0x00000100\r
8829 HIGH_PRIORITY_CLASS         0x00000080\r
8830 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8831 NORMAL_PRIORITY_CLASS       0x00000020\r
8832 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8833 IDLE_PRIORITY_CLASS         0x00000040\r
8834 */\r
8835         if (nice < -15) return 0x00000080;\r
8836         if (nice < 0)   return 0x00008000;\r
8837         if (nice == 0)  return 0x00000020;\r
8838         if (nice < 15)  return 0x00004000;\r
8839         return 0x00000040;\r
8840 }\r
8841 \r
8842 /* Start a child process running the given program.\r
8843    The process's standard output can be read from "from", and its\r
8844    standard input can be written to "to".\r
8845    Exit with fatal error if anything goes wrong.\r
8846    Returns an opaque pointer that can be used to destroy the process\r
8847    later.\r
8848 */\r
8849 int\r
8850 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8851 {\r
8852 #define BUFSIZE 4096\r
8853 \r
8854   HANDLE hChildStdinRd, hChildStdinWr,\r
8855     hChildStdoutRd, hChildStdoutWr;\r
8856   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8857   SECURITY_ATTRIBUTES saAttr;\r
8858   BOOL fSuccess;\r
8859   PROCESS_INFORMATION piProcInfo;\r
8860   STARTUPINFO siStartInfo;\r
8861   ChildProc *cp;\r
8862   char buf[MSG_SIZ];\r
8863   DWORD err;\r
8864 \r
8865   if (appData.debugMode) {\r
8866     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8867   }\r
8868 \r
8869   *pr = NoProc;\r
8870 \r
8871   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8872   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8873   saAttr.bInheritHandle = TRUE;\r
8874   saAttr.lpSecurityDescriptor = NULL;\r
8875 \r
8876   /*\r
8877    * The steps for redirecting child's STDOUT:\r
8878    *     1. Create anonymous pipe to be STDOUT for child.\r
8879    *     2. Create a noninheritable duplicate of read handle,\r
8880    *         and close the inheritable read handle.\r
8881    */\r
8882 \r
8883   /* Create a pipe for the child's STDOUT. */\r
8884   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8885     return GetLastError();\r
8886   }\r
8887 \r
8888   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8889   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8890                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8891                              FALSE,     /* not inherited */\r
8892                              DUPLICATE_SAME_ACCESS);\r
8893   if (! fSuccess) {\r
8894     return GetLastError();\r
8895   }\r
8896   CloseHandle(hChildStdoutRd);\r
8897 \r
8898   /*\r
8899    * The steps for redirecting child's STDIN:\r
8900    *     1. Create anonymous pipe to be STDIN for child.\r
8901    *     2. Create a noninheritable duplicate of write handle,\r
8902    *         and close the inheritable write handle.\r
8903    */\r
8904 \r
8905   /* Create a pipe for the child's STDIN. */\r
8906   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8907     return GetLastError();\r
8908   }\r
8909 \r
8910   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8911   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8912                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8913                              FALSE,     /* not inherited */\r
8914                              DUPLICATE_SAME_ACCESS);\r
8915   if (! fSuccess) {\r
8916     return GetLastError();\r
8917   }\r
8918   CloseHandle(hChildStdinWr);\r
8919 \r
8920   /* Arrange to (1) look in dir for the child .exe file, and\r
8921    * (2) have dir be the child's working directory.  Interpret\r
8922    * dir relative to the directory WinBoard loaded from. */\r
8923   GetCurrentDirectory(MSG_SIZ, buf);\r
8924   SetCurrentDirectory(installDir);\r
8925   SetCurrentDirectory(dir);\r
8926 \r
8927   /* Now create the child process. */\r
8928 \r
8929   siStartInfo.cb = sizeof(STARTUPINFO);\r
8930   siStartInfo.lpReserved = NULL;\r
8931   siStartInfo.lpDesktop = NULL;\r
8932   siStartInfo.lpTitle = NULL;\r
8933   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8934   siStartInfo.cbReserved2 = 0;\r
8935   siStartInfo.lpReserved2 = NULL;\r
8936   siStartInfo.hStdInput = hChildStdinRd;\r
8937   siStartInfo.hStdOutput = hChildStdoutWr;\r
8938   siStartInfo.hStdError = hChildStdoutWr;\r
8939 \r
8940   fSuccess = CreateProcess(NULL,\r
8941                            cmdLine,        /* command line */\r
8942                            NULL,           /* process security attributes */\r
8943                            NULL,           /* primary thread security attrs */\r
8944                            TRUE,           /* handles are inherited */\r
8945                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8946                            NULL,           /* use parent's environment */\r
8947                            NULL,\r
8948                            &siStartInfo, /* STARTUPINFO pointer */\r
8949                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8950 \r
8951   err = GetLastError();\r
8952   SetCurrentDirectory(buf); /* return to prev directory */\r
8953   if (! fSuccess) {\r
8954     return err;\r
8955   }\r
8956 \r
8957   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8958     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8959     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8960   }\r
8961 \r
8962   /* Close the handles we don't need in the parent */\r
8963   CloseHandle(piProcInfo.hThread);\r
8964   CloseHandle(hChildStdinRd);\r
8965   CloseHandle(hChildStdoutWr);\r
8966 \r
8967   /* Prepare return value */\r
8968   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8969   cp->kind = CPReal;\r
8970   cp->hProcess = piProcInfo.hProcess;\r
8971   cp->pid = piProcInfo.dwProcessId;\r
8972   cp->hFrom = hChildStdoutRdDup;\r
8973   cp->hTo = hChildStdinWrDup;\r
8974 \r
8975   *pr = (void *) cp;\r
8976 \r
8977   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8978      2000 where engines sometimes don't see the initial command(s)\r
8979      from WinBoard and hang.  I don't understand how that can happen,\r
8980      but the Sleep is harmless, so I've put it in.  Others have also\r
8981      reported what may be the same problem, so hopefully this will fix\r
8982      it for them too.  */\r
8983   Sleep(500);\r
8984 \r
8985   return NO_ERROR;\r
8986 }\r
8987 \r
8988 \r
8989 void\r
8990 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8991 {\r
8992   ChildProc *cp; int result;\r
8993 \r
8994   cp = (ChildProc *) pr;\r
8995   if (cp == NULL) return;\r
8996 \r
8997   switch (cp->kind) {\r
8998   case CPReal:\r
8999     /* TerminateProcess is considered harmful, so... */\r
9000     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9001     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9002     /* The following doesn't work because the chess program\r
9003        doesn't "have the same console" as WinBoard.  Maybe\r
9004        we could arrange for this even though neither WinBoard\r
9005        nor the chess program uses a console for stdio? */\r
9006     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9007 \r
9008     /* [AS] Special termination modes for misbehaving programs... */\r
9009     if( signal == 9 ) { \r
9010         result = TerminateProcess( cp->hProcess, 0 );\r
9011 \r
9012         if ( appData.debugMode) {\r
9013             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9014         }\r
9015     }\r
9016     else if( signal == 10 ) {\r
9017         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9018 \r
9019         if( dw != WAIT_OBJECT_0 ) {\r
9020             result = TerminateProcess( cp->hProcess, 0 );\r
9021 \r
9022             if ( appData.debugMode) {\r
9023                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9024             }\r
9025 \r
9026         }\r
9027     }\r
9028 \r
9029     CloseHandle(cp->hProcess);\r
9030     break;\r
9031 \r
9032   case CPComm:\r
9033     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9034     break;\r
9035 \r
9036   case CPSock:\r
9037     closesocket(cp->sock);\r
9038     WSACleanup();\r
9039     break;\r
9040 \r
9041   case CPRcmd:\r
9042     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9043     closesocket(cp->sock);\r
9044     closesocket(cp->sock2);\r
9045     WSACleanup();\r
9046     break;\r
9047   }\r
9048   free(cp);\r
9049 }\r
9050 \r
9051 void\r
9052 InterruptChildProcess(ProcRef pr)\r
9053 {\r
9054   ChildProc *cp;\r
9055 \r
9056   cp = (ChildProc *) pr;\r
9057   if (cp == NULL) return;\r
9058   switch (cp->kind) {\r
9059   case CPReal:\r
9060     /* The following doesn't work because the chess program\r
9061        doesn't "have the same console" as WinBoard.  Maybe\r
9062        we could arrange for this even though neither WinBoard\r
9063        nor the chess program uses a console for stdio */\r
9064     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9065     break;\r
9066 \r
9067   case CPComm:\r
9068   case CPSock:\r
9069     /* Can't interrupt */\r
9070     break;\r
9071 \r
9072   case CPRcmd:\r
9073     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9074     break;\r
9075   }\r
9076 }\r
9077 \r
9078 \r
9079 int\r
9080 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9081 {\r
9082   char cmdLine[MSG_SIZ];\r
9083 \r
9084   if (port[0] == NULLCHAR) {\r
9085     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9086   } else {\r
9087     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9088   }\r
9089   return StartChildProcess(cmdLine, "", pr);\r
9090 }\r
9091 \r
9092 \r
9093 /* Code to open TCP sockets */\r
9094 \r
9095 int\r
9096 OpenTCP(char *host, char *port, ProcRef *pr)\r
9097 {\r
9098   ChildProc *cp;\r
9099   int err;\r
9100   SOCKET s;\r
9101   struct sockaddr_in sa, mysa;\r
9102   struct hostent FAR *hp;\r
9103   unsigned short uport;\r
9104   WORD wVersionRequested;\r
9105   WSADATA wsaData;\r
9106 \r
9107   /* Initialize socket DLL */\r
9108   wVersionRequested = MAKEWORD(1, 1);\r
9109   err = WSAStartup(wVersionRequested, &wsaData);\r
9110   if (err != 0) return err;\r
9111 \r
9112   /* Make socket */\r
9113   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9114     err = WSAGetLastError();\r
9115     WSACleanup();\r
9116     return err;\r
9117   }\r
9118 \r
9119   /* Bind local address using (mostly) don't-care values.\r
9120    */\r
9121   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9122   mysa.sin_family = AF_INET;\r
9123   mysa.sin_addr.s_addr = INADDR_ANY;\r
9124   uport = (unsigned short) 0;\r
9125   mysa.sin_port = htons(uport);\r
9126   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9127       == SOCKET_ERROR) {\r
9128     err = WSAGetLastError();\r
9129     WSACleanup();\r
9130     return err;\r
9131   }\r
9132 \r
9133   /* Resolve remote host name */\r
9134   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9135   if (!(hp = gethostbyname(host))) {\r
9136     unsigned int b0, b1, b2, b3;\r
9137 \r
9138     err = WSAGetLastError();\r
9139 \r
9140     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9141       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9142       hp->h_addrtype = AF_INET;\r
9143       hp->h_length = 4;\r
9144       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9145       hp->h_addr_list[0] = (char *) malloc(4);\r
9146       hp->h_addr_list[0][0] = (char) b0;\r
9147       hp->h_addr_list[0][1] = (char) b1;\r
9148       hp->h_addr_list[0][2] = (char) b2;\r
9149       hp->h_addr_list[0][3] = (char) b3;\r
9150     } else {\r
9151       WSACleanup();\r
9152       return err;\r
9153     }\r
9154   }\r
9155   sa.sin_family = hp->h_addrtype;\r
9156   uport = (unsigned short) atoi(port);\r
9157   sa.sin_port = htons(uport);\r
9158   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9159 \r
9160   /* Make connection */\r
9161   if (connect(s, (struct sockaddr *) &sa,\r
9162               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9163     err = WSAGetLastError();\r
9164     WSACleanup();\r
9165     return err;\r
9166   }\r
9167 \r
9168   /* Prepare return value */\r
9169   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9170   cp->kind = CPSock;\r
9171   cp->sock = s;\r
9172   *pr = (ProcRef *) cp;\r
9173 \r
9174   return NO_ERROR;\r
9175 }\r
9176 \r
9177 int\r
9178 OpenCommPort(char *name, ProcRef *pr)\r
9179 {\r
9180   HANDLE h;\r
9181   COMMTIMEOUTS ct;\r
9182   ChildProc *cp;\r
9183   char fullname[MSG_SIZ];\r
9184 \r
9185   if (*name != '\\')\r
9186     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9187   else\r
9188     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9189 \r
9190   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9191                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9192   if (h == (HANDLE) -1) {\r
9193     return GetLastError();\r
9194   }\r
9195   hCommPort = h;\r
9196 \r
9197   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9198 \r
9199   /* Accumulate characters until a 100ms pause, then parse */\r
9200   ct.ReadIntervalTimeout = 100;\r
9201   ct.ReadTotalTimeoutMultiplier = 0;\r
9202   ct.ReadTotalTimeoutConstant = 0;\r
9203   ct.WriteTotalTimeoutMultiplier = 0;\r
9204   ct.WriteTotalTimeoutConstant = 0;\r
9205   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9206 \r
9207   /* Prepare return value */\r
9208   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9209   cp->kind = CPComm;\r
9210   cp->hFrom = h;\r
9211   cp->hTo = h;\r
9212   *pr = (ProcRef *) cp;\r
9213 \r
9214   return NO_ERROR;\r
9215 }\r
9216 \r
9217 int\r
9218 OpenLoopback(ProcRef *pr)\r
9219 {\r
9220   DisplayFatalError(_("Not implemented"), 0, 1);\r
9221   return NO_ERROR;\r
9222 }\r
9223 \r
9224 \r
9225 int\r
9226 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9227 {\r
9228   ChildProc *cp;\r
9229   int err;\r
9230   SOCKET s, s2, s3;\r
9231   struct sockaddr_in sa, mysa;\r
9232   struct hostent FAR *hp;\r
9233   unsigned short uport;\r
9234   WORD wVersionRequested;\r
9235   WSADATA wsaData;\r
9236   int fromPort;\r
9237   char stderrPortStr[MSG_SIZ];\r
9238 \r
9239   /* Initialize socket DLL */\r
9240   wVersionRequested = MAKEWORD(1, 1);\r
9241   err = WSAStartup(wVersionRequested, &wsaData);\r
9242   if (err != 0) return err;\r
9243 \r
9244   /* Resolve remote host name */\r
9245   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9246   if (!(hp = gethostbyname(host))) {\r
9247     unsigned int b0, b1, b2, b3;\r
9248 \r
9249     err = WSAGetLastError();\r
9250 \r
9251     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9252       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9253       hp->h_addrtype = AF_INET;\r
9254       hp->h_length = 4;\r
9255       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9256       hp->h_addr_list[0] = (char *) malloc(4);\r
9257       hp->h_addr_list[0][0] = (char) b0;\r
9258       hp->h_addr_list[0][1] = (char) b1;\r
9259       hp->h_addr_list[0][2] = (char) b2;\r
9260       hp->h_addr_list[0][3] = (char) b3;\r
9261     } else {\r
9262       WSACleanup();\r
9263       return err;\r
9264     }\r
9265   }\r
9266   sa.sin_family = hp->h_addrtype;\r
9267   uport = (unsigned short) 514;\r
9268   sa.sin_port = htons(uport);\r
9269   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9270 \r
9271   /* Bind local socket to unused "privileged" port address\r
9272    */\r
9273   s = INVALID_SOCKET;\r
9274   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9275   mysa.sin_family = AF_INET;\r
9276   mysa.sin_addr.s_addr = INADDR_ANY;\r
9277   for (fromPort = 1023;; fromPort--) {\r
9278     if (fromPort < 0) {\r
9279       WSACleanup();\r
9280       return WSAEADDRINUSE;\r
9281     }\r
9282     if (s == INVALID_SOCKET) {\r
9283       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9284         err = WSAGetLastError();\r
9285         WSACleanup();\r
9286         return err;\r
9287       }\r
9288     }\r
9289     uport = (unsigned short) fromPort;\r
9290     mysa.sin_port = htons(uport);\r
9291     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9292         == SOCKET_ERROR) {\r
9293       err = WSAGetLastError();\r
9294       if (err == WSAEADDRINUSE) continue;\r
9295       WSACleanup();\r
9296       return err;\r
9297     }\r
9298     if (connect(s, (struct sockaddr *) &sa,\r
9299       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9300       err = WSAGetLastError();\r
9301       if (err == WSAEADDRINUSE) {\r
9302         closesocket(s);\r
9303         s = -1;\r
9304         continue;\r
9305       }\r
9306       WSACleanup();\r
9307       return err;\r
9308     }\r
9309     break;\r
9310   }\r
9311 \r
9312   /* Bind stderr local socket to unused "privileged" port address\r
9313    */\r
9314   s2 = INVALID_SOCKET;\r
9315   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9316   mysa.sin_family = AF_INET;\r
9317   mysa.sin_addr.s_addr = INADDR_ANY;\r
9318   for (fromPort = 1023;; fromPort--) {\r
9319     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9320     if (fromPort < 0) {\r
9321       (void) closesocket(s);\r
9322       WSACleanup();\r
9323       return WSAEADDRINUSE;\r
9324     }\r
9325     if (s2 == INVALID_SOCKET) {\r
9326       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9327         err = WSAGetLastError();\r
9328         closesocket(s);\r
9329         WSACleanup();\r
9330         return err;\r
9331       }\r
9332     }\r
9333     uport = (unsigned short) fromPort;\r
9334     mysa.sin_port = htons(uport);\r
9335     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9336         == SOCKET_ERROR) {\r
9337       err = WSAGetLastError();\r
9338       if (err == WSAEADDRINUSE) continue;\r
9339       (void) closesocket(s);\r
9340       WSACleanup();\r
9341       return err;\r
9342     }\r
9343     if (listen(s2, 1) == SOCKET_ERROR) {\r
9344       err = WSAGetLastError();\r
9345       if (err == WSAEADDRINUSE) {\r
9346         closesocket(s2);\r
9347         s2 = INVALID_SOCKET;\r
9348         continue;\r
9349       }\r
9350       (void) closesocket(s);\r
9351       (void) closesocket(s2);\r
9352       WSACleanup();\r
9353       return err;\r
9354     }\r
9355     break;\r
9356   }\r
9357   prevStderrPort = fromPort; // remember port used\r
9358   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9359 \r
9360   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9361     err = WSAGetLastError();\r
9362     (void) closesocket(s);\r
9363     (void) closesocket(s2);\r
9364     WSACleanup();\r
9365     return err;\r
9366   }\r
9367 \r
9368   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9369     err = WSAGetLastError();\r
9370     (void) closesocket(s);\r
9371     (void) closesocket(s2);\r
9372     WSACleanup();\r
9373     return err;\r
9374   }\r
9375   if (*user == NULLCHAR) user = UserName();\r
9376   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9377     err = WSAGetLastError();\r
9378     (void) closesocket(s);\r
9379     (void) closesocket(s2);\r
9380     WSACleanup();\r
9381     return err;\r
9382   }\r
9383   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9384     err = WSAGetLastError();\r
9385     (void) closesocket(s);\r
9386     (void) closesocket(s2);\r
9387     WSACleanup();\r
9388     return err;\r
9389   }\r
9390 \r
9391   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9392     err = WSAGetLastError();\r
9393     (void) closesocket(s);\r
9394     (void) closesocket(s2);\r
9395     WSACleanup();\r
9396     return err;\r
9397   }\r
9398   (void) closesocket(s2);  /* Stop listening */\r
9399 \r
9400   /* Prepare return value */\r
9401   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9402   cp->kind = CPRcmd;\r
9403   cp->sock = s;\r
9404   cp->sock2 = s3;\r
9405   *pr = (ProcRef *) cp;\r
9406 \r
9407   return NO_ERROR;\r
9408 }\r
9409 \r
9410 \r
9411 InputSourceRef\r
9412 AddInputSource(ProcRef pr, int lineByLine,\r
9413                InputCallback func, VOIDSTAR closure)\r
9414 {\r
9415   InputSource *is, *is2 = NULL;\r
9416   ChildProc *cp = (ChildProc *) pr;\r
9417 \r
9418   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9419   is->lineByLine = lineByLine;\r
9420   is->func = func;\r
9421   is->closure = closure;\r
9422   is->second = NULL;\r
9423   is->next = is->buf;\r
9424   if (pr == NoProc) {\r
9425     is->kind = CPReal;\r
9426     consoleInputSource = is;\r
9427   } else {\r
9428     is->kind = cp->kind;\r
9429     /* \r
9430         [AS] Try to avoid a race condition if the thread is given control too early:\r
9431         we create all threads suspended so that the is->hThread variable can be\r
9432         safely assigned, then let the threads start with ResumeThread.\r
9433     */\r
9434     switch (cp->kind) {\r
9435     case CPReal:\r
9436       is->hFile = cp->hFrom;\r
9437       cp->hFrom = NULL; /* now owned by InputThread */\r
9438       is->hThread =\r
9439         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9440                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9441       break;\r
9442 \r
9443     case CPComm:\r
9444       is->hFile = cp->hFrom;\r
9445       cp->hFrom = NULL; /* now owned by InputThread */\r
9446       is->hThread =\r
9447         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9448                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9449       break;\r
9450 \r
9451     case CPSock:\r
9452       is->sock = cp->sock;\r
9453       is->hThread =\r
9454         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9455                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9456       break;\r
9457 \r
9458     case CPRcmd:\r
9459       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9460       *is2 = *is;\r
9461       is->sock = cp->sock;\r
9462       is->second = is2;\r
9463       is2->sock = cp->sock2;\r
9464       is2->second = is2;\r
9465       is->hThread =\r
9466         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9467                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9468       is2->hThread =\r
9469         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9470                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9471       break;\r
9472     }\r
9473 \r
9474     if( is->hThread != NULL ) {\r
9475         ResumeThread( is->hThread );\r
9476     }\r
9477 \r
9478     if( is2 != NULL && is2->hThread != NULL ) {\r
9479         ResumeThread( is2->hThread );\r
9480     }\r
9481   }\r
9482 \r
9483   return (InputSourceRef) is;\r
9484 }\r
9485 \r
9486 void\r
9487 RemoveInputSource(InputSourceRef isr)\r
9488 {\r
9489   InputSource *is;\r
9490 \r
9491   is = (InputSource *) isr;\r
9492   is->hThread = NULL;  /* tell thread to stop */\r
9493   CloseHandle(is->hThread);\r
9494   if (is->second != NULL) {\r
9495     is->second->hThread = NULL;\r
9496     CloseHandle(is->second->hThread);\r
9497   }\r
9498 }\r
9499 \r
9500 int no_wrap(char *message, int count)\r
9501 {\r
9502     ConsoleOutput(message, count, FALSE);\r
9503     return count;\r
9504 }\r
9505 \r
9506 int\r
9507 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9508 {\r
9509   DWORD dOutCount;\r
9510   int outCount = SOCKET_ERROR;\r
9511   ChildProc *cp = (ChildProc *) pr;\r
9512   static OVERLAPPED ovl;\r
9513   static int line = 0;\r
9514 \r
9515   if (pr == NoProc)\r
9516   {\r
9517     if (appData.noJoin || !appData.useInternalWrap)\r
9518       return no_wrap(message, count);\r
9519     else\r
9520     {\r
9521       int width = get_term_width();\r
9522       int len = wrap(NULL, message, count, width, &line);\r
9523       char *msg = malloc(len);\r
9524       int dbgchk;\r
9525 \r
9526       if (!msg)\r
9527         return no_wrap(message, count);\r
9528       else\r
9529       {\r
9530         dbgchk = wrap(msg, message, count, width, &line);\r
9531         if (dbgchk != len && appData.debugMode)\r
9532             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9533         ConsoleOutput(msg, len, FALSE);\r
9534         free(msg);\r
9535         return len;\r
9536       }\r
9537     }\r
9538   }\r
9539 \r
9540   if (ovl.hEvent == NULL) {\r
9541     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9542   }\r
9543   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9544 \r
9545   switch (cp->kind) {\r
9546   case CPSock:\r
9547   case CPRcmd:\r
9548     outCount = send(cp->sock, message, count, 0);\r
9549     if (outCount == SOCKET_ERROR) {\r
9550       *outError = WSAGetLastError();\r
9551     } else {\r
9552       *outError = NO_ERROR;\r
9553     }\r
9554     break;\r
9555 \r
9556   case CPReal:\r
9557     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9558                   &dOutCount, NULL)) {\r
9559       *outError = NO_ERROR;\r
9560       outCount = (int) dOutCount;\r
9561     } else {\r
9562       *outError = GetLastError();\r
9563     }\r
9564     break;\r
9565 \r
9566   case CPComm:\r
9567     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9568                             &dOutCount, &ovl);\r
9569     if (*outError == NO_ERROR) {\r
9570       outCount = (int) dOutCount;\r
9571     }\r
9572     break;\r
9573   }\r
9574   return outCount;\r
9575 }\r
9576 \r
9577 int\r
9578 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9579                        long msdelay)\r
9580 {\r
9581   /* Ignore delay, not implemented for WinBoard */\r
9582   return OutputToProcess(pr, message, count, outError);\r
9583 }\r
9584 \r
9585 \r
9586 void\r
9587 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9588                         char *buf, int count, int error)\r
9589 {\r
9590   DisplayFatalError(_("Not implemented"), 0, 1);\r
9591 }\r
9592 \r
9593 /* see wgamelist.c for Game List functions */\r
9594 /* see wedittags.c for Edit Tags functions */\r
9595 \r
9596 \r
9597 VOID\r
9598 ICSInitScript()\r
9599 {\r
9600   FILE *f;\r
9601   char buf[MSG_SIZ];\r
9602   char *dummy;\r
9603 \r
9604   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9605     f = fopen(buf, "r");\r
9606     if (f != NULL) {\r
9607       ProcessICSInitScript(f);\r
9608       fclose(f);\r
9609     }\r
9610   }\r
9611 }\r
9612 \r
9613 \r
9614 VOID\r
9615 StartAnalysisClock()\r
9616 {\r
9617   if (analysisTimerEvent) return;\r
9618   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9619                                         (UINT) 2000, NULL);\r
9620 }\r
9621 \r
9622 VOID\r
9623 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9624 {\r
9625   highlightInfo.sq[0].x = fromX;\r
9626   highlightInfo.sq[0].y = fromY;\r
9627   highlightInfo.sq[1].x = toX;\r
9628   highlightInfo.sq[1].y = toY;\r
9629 }\r
9630 \r
9631 VOID\r
9632 ClearHighlights()\r
9633 {\r
9634   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9635     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9636 }\r
9637 \r
9638 VOID\r
9639 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9640 {\r
9641   premoveHighlightInfo.sq[0].x = fromX;\r
9642   premoveHighlightInfo.sq[0].y = fromY;\r
9643   premoveHighlightInfo.sq[1].x = toX;\r
9644   premoveHighlightInfo.sq[1].y = toY;\r
9645 }\r
9646 \r
9647 VOID\r
9648 ClearPremoveHighlights()\r
9649 {\r
9650   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9651     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9652 }\r
9653 \r
9654 VOID\r
9655 ShutDownFrontEnd()\r
9656 {\r
9657   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9658   DeleteClipboardTempFiles();\r
9659 }\r
9660 \r
9661 void\r
9662 BoardToTop()\r
9663 {\r
9664     if (IsIconic(hwndMain))\r
9665       ShowWindow(hwndMain, SW_RESTORE);\r
9666 \r
9667     SetActiveWindow(hwndMain);\r
9668 }\r
9669 \r
9670 /*\r
9671  * Prototypes for animation support routines\r
9672  */\r
9673 static void ScreenSquare(int column, int row, POINT * pt);\r
9674 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9675      POINT frames[], int * nFrames);\r
9676 \r
9677 \r
9678 #define kFactor 4\r
9679 \r
9680 void\r
9681 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9682 {       // [HGM] atomic: animate blast wave\r
9683         int i;\r
9684 \r
9685         explodeInfo.fromX = fromX;\r
9686         explodeInfo.fromY = fromY;\r
9687         explodeInfo.toX = toX;\r
9688         explodeInfo.toY = toY;\r
9689         for(i=1; i<4*kFactor; i++) {\r
9690             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9691             DrawPosition(FALSE, board);\r
9692             Sleep(appData.animSpeed);\r
9693         }\r
9694         explodeInfo.radius = 0;\r
9695         DrawPosition(TRUE, board);\r
9696 }\r
9697 \r
9698 void\r
9699 AnimateMove(board, fromX, fromY, toX, toY)\r
9700      Board board;\r
9701      int fromX;\r
9702      int fromY;\r
9703      int toX;\r
9704      int toY;\r
9705 {\r
9706   ChessSquare piece;\r
9707   POINT start, finish, mid;\r
9708   POINT frames[kFactor * 2 + 1];\r
9709   int nFrames, n;\r
9710 \r
9711   if (!appData.animate) return;\r
9712   if (doingSizing) return;\r
9713   if (fromY < 0 || fromX < 0) return;\r
9714   piece = board[fromY][fromX];\r
9715   if (piece >= EmptySquare) return;\r
9716 \r
9717   ScreenSquare(fromX, fromY, &start);\r
9718   ScreenSquare(toX, toY, &finish);\r
9719 \r
9720   /* All moves except knight jumps move in straight line */\r
9721   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9722     mid.x = start.x + (finish.x - start.x) / 2;\r
9723     mid.y = start.y + (finish.y - start.y) / 2;\r
9724   } else {\r
9725     /* Knight: make straight movement then diagonal */\r
9726     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9727        mid.x = start.x + (finish.x - start.x) / 2;\r
9728        mid.y = start.y;\r
9729      } else {\r
9730        mid.x = start.x;\r
9731        mid.y = start.y + (finish.y - start.y) / 2;\r
9732      }\r
9733   }\r
9734   \r
9735   /* Don't use as many frames for very short moves */\r
9736   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9737     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9738   else\r
9739     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9740 \r
9741   animInfo.from.x = fromX;\r
9742   animInfo.from.y = fromY;\r
9743   animInfo.to.x = toX;\r
9744   animInfo.to.y = toY;\r
9745   animInfo.lastpos = start;\r
9746   animInfo.piece = piece;\r
9747   for (n = 0; n < nFrames; n++) {\r
9748     animInfo.pos = frames[n];\r
9749     DrawPosition(FALSE, NULL);\r
9750     animInfo.lastpos = animInfo.pos;\r
9751     Sleep(appData.animSpeed);\r
9752   }\r
9753   animInfo.pos = finish;\r
9754   DrawPosition(FALSE, NULL);\r
9755   animInfo.piece = EmptySquare;\r
9756   Explode(board, fromX, fromY, toX, toY);\r
9757 }\r
9758 \r
9759 /*      Convert board position to corner of screen rect and color       */\r
9760 \r
9761 static void\r
9762 ScreenSquare(column, row, pt)\r
9763      int column; int row; POINT * pt;\r
9764 {\r
9765   if (flipView) {\r
9766     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9767     pt->y = lineGap + row * (squareSize + lineGap);\r
9768   } else {\r
9769     pt->x = lineGap + column * (squareSize + lineGap);\r
9770     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9771   }\r
9772 }\r
9773 \r
9774 /*      Generate a series of frame coords from start->mid->finish.\r
9775         The movement rate doubles until the half way point is\r
9776         reached, then halves back down to the final destination,\r
9777         which gives a nice slow in/out effect. The algorithmn\r
9778         may seem to generate too many intermediates for short\r
9779         moves, but remember that the purpose is to attract the\r
9780         viewers attention to the piece about to be moved and\r
9781         then to where it ends up. Too few frames would be less\r
9782         noticeable.                                             */\r
9783 \r
9784 static void\r
9785 Tween(start, mid, finish, factor, frames, nFrames)\r
9786      POINT * start; POINT * mid;\r
9787      POINT * finish; int factor;\r
9788      POINT frames[]; int * nFrames;\r
9789 {\r
9790   int n, fraction = 1, count = 0;\r
9791 \r
9792   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9793   for (n = 0; n < factor; n++)\r
9794     fraction *= 2;\r
9795   for (n = 0; n < factor; n++) {\r
9796     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9797     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9798     count ++;\r
9799     fraction = fraction / 2;\r
9800   }\r
9801   \r
9802   /* Midpoint */\r
9803   frames[count] = *mid;\r
9804   count ++;\r
9805   \r
9806   /* Slow out, stepping 1/2, then 1/4, ... */\r
9807   fraction = 2;\r
9808   for (n = 0; n < factor; n++) {\r
9809     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9810     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9811     count ++;\r
9812     fraction = fraction * 2;\r
9813   }\r
9814   *nFrames = count;\r
9815 }\r
9816 \r
9817 void\r
9818 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9819 {\r
9820     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9821 \r
9822     EvalGraphSet( first, last, current, pvInfoList );\r
9823 }\r
9824 \r
9825 void\r
9826 SettingsPopUp(ChessProgramState *cps)\r
9827 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9828       EngineOptionsPopup(savedHwnd, cps);\r
9829 }\r