Updated copyright notice to 2011
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void DisplayMove P((int moveNumber));\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
110 void ChatPopUp P((char *s));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
138 \r
139 typedef struct { // [HGM] atomic\r
140   int fromX, fromY, toX, toY, radius;\r
141 } ExplodeInfo;\r
142 \r
143 static ExplodeInfo explodeInfo;\r
144 \r
145 /* Window class names */\r
146 char szAppName[] = "WinBoard";\r
147 char szConsoleName[] = "WBConsole";\r
148 \r
149 /* Title bar text */\r
150 char szTitle[] = "WinBoard";\r
151 char szConsoleTitle[] = "I C S Interaction";\r
152 \r
153 char *programName;\r
154 char *settingsFileName;\r
155 Boolean saveSettingsOnExit;\r
156 char installDir[MSG_SIZ];\r
157 int errorExitStatus;\r
158 \r
159 BoardSize boardSize;\r
160 Boolean chessProgram;\r
161 //static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 int squareSize, lineGap, minorSize;\r
164 static int winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define PALETTESIZE 256\r
180 \r
181 HINSTANCE hInst;          /* current instance */\r
182 Boolean alwaysOnTop = FALSE;\r
183 RECT boardRect;\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
185   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
186 HPALETTE hPal;\r
187 ColorClass currentColorClass;\r
188 \r
189 static HWND savedHwnd;\r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   markerBrush,      /* [HGM] markers */\r
197   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
200 static HPEN gridPen = NULL;\r
201 static HPEN highlightPen = NULL;\r
202 static HPEN premovePen = NULL;\r
203 static NPLOGPALETTE pLogPal;\r
204 static BOOL paletteChanged = FALSE;\r
205 static HICON iconWhite, iconBlack, iconCurrent;\r
206 static int doingSizing = FALSE;\r
207 static int lastSizing = 0;\r
208 static int prevStderrPort;\r
209 static HBITMAP userLogo;\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #if defined(_winmajor)\r
222 #define oldDialog (_winmajor < 4)\r
223 #else\r
224 #define oldDialog 0\r
225 #endif\r
226 #endif\r
227 \r
228 #define INTERNATIONAL\r
229 \r
230 #ifdef INTERNATIONAL\r
231 #  define _(s) T_(s)\r
232 #  define N_(s) s\r
233 #else\r
234 #  define _(s) s\r
235 #  define N_(s) s\r
236 #  define T_(s) s\r
237 #  define Translate(x, y)\r
238 #  define LoadLanguageFile(s)\r
239 #endif\r
240 \r
241 #ifdef INTERNATIONAL\r
242 \r
243 Boolean barbaric; // flag indicating if translation is needed\r
244 \r
245 // list of item numbers used in each dialog (used to alter language at run time)\r
246 \r
247 #define ABOUTBOX -1  /* not sure why these are needed */\r
248 #define ABOUTBOX2 -1\r
249 \r
250 int dialogItems[][40] = {\r
251 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][10];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
349     if((f = fopen(buf, "r")) == NULL) return;\r
350     while((k = fgetc(f)) != EOF) {\r
351         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
352         languageBuf[i] = k;\r
353         if(k == '\n') {\r
354             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
355                 char *p;\r
356                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
357                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
358                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
359                         english[j] = languageBuf + n + 1; *p = 0;\r
360                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
362                     }\r
363                 }\r
364             }\r
365             n = i + 1;\r
366         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
367             switch(k) {\r
368               case 'n': k = '\n'; break;\r
369               case 'r': k = '\r'; break;\r
370               case 't': k = '\t'; break;\r
371             }\r
372             languageBuf[--i] = k;\r
373         }\r
374         i++;\r
375     }\r
376     fclose(f);\r
377     barbaric = (j != 0);\r
378     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
379 }\r
380 \r
381 char *\r
382 T_(char *s)\r
383 {   // return the translation of the given string\r
384     // efficiency can be improved a lot...\r
385     int i=0;\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
387     if(!barbaric) return s;\r
388     if(!s) return ""; // sanity\r
389     while(english[i]) {\r
390         if(!strcmp(s, english[i])) return foreign[i];\r
391         i++;\r
392     }\r
393     return s;\r
394 }\r
395 \r
396 void\r
397 Translate(HWND hDlg, int dialogID)\r
398 {   // translate all text items in the given dialog\r
399     int i=0, j, k;\r
400     char buf[MSG_SIZ], *s;\r
401     if(!barbaric) return;\r
402     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
403     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
404     GetWindowText( hDlg, buf, MSG_SIZ );\r
405     s = T_(buf);\r
406     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
407     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
408         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
409         if(strlen(buf) == 0) continue;\r
410         s = T_(buf);\r
411         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
412     }\r
413 }\r
414 \r
415 HMENU\r
416 TranslateOneMenu(int i, HMENU subMenu)\r
417 {\r
418     int j;\r
419     static MENUITEMINFO info;\r
420 \r
421     info.cbSize = sizeof(MENUITEMINFO);\r
422     info.fMask = MIIM_STATE | MIIM_TYPE;\r
423           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
424             char buf[MSG_SIZ];\r
425             info.dwTypeData = buf;\r
426             info.cch = sizeof(buf);\r
427             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
428             if(i < 10) {
429                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
430                 else menuText[i][j] = strdup(buf); // remember original on first change\r
431             }\r
432             if(buf[0] == NULLCHAR) continue;\r
433             info.dwTypeData = T_(buf);\r
434             info.cch = strlen(buf)+1;\r
435             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
436           }\r
437     return subMenu;\r
438 }\r
439 \r
440 void\r
441 TranslateMenus(int addLanguage)\r
442 {\r
443     int i;\r
444     WIN32_FIND_DATA fileData;\r
445     HANDLE hFind;\r
446 #define IDM_English 1895\r
447     if(1) {\r
448         HMENU mainMenu = GetMenu(hwndMain);\r
449         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
450           HMENU subMenu = GetSubMenu(mainMenu, i);\r
451           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
452                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
453           TranslateOneMenu(i, subMenu);\r
454         }\r
455         DrawMenuBar(hwndMain);\r
456     }\r
457 \r
458     if(!addLanguage) return;\r
459     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
462         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
463         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
464         i = 0; lastChecked = IDM_English;\r
465         do {\r
466             char *p, *q = fileData.cFileName;\r
467             int checkFlag = MF_UNCHECKED;\r
468             languageFile[i] = strdup(q);\r
469             if(barbaric && !strcmp(oldLanguage, q)) {\r
470                 checkFlag = MF_CHECKED;\r
471                 lastChecked = IDM_English + i + 1;\r
472                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
473             }\r
474             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
475             p = strstr(fileData.cFileName, ".lng");\r
476             if(p) *p = 0;\r
477             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
478         } while(FindNextFile(hFind, &fileData));\r
479         FindClose(hFind);\r
480     }\r
481 }\r
482 \r
483 #endif\r
484 \r
485 typedef struct {\r
486   char *name;\r
487   int squareSize;\r
488   int lineGap;\r
489   int smallLayout;\r
490   int tinyLayout;\r
491   int cliWidth, cliHeight;\r
492 } SizeInfo;\r
493 \r
494 SizeInfo sizeInfo[] = \r
495 {\r
496   { "tiny",     21, 0, 1, 1, 0, 0 },\r
497   { "teeny",    25, 1, 1, 1, 0, 0 },\r
498   { "dinky",    29, 1, 1, 1, 0, 0 },\r
499   { "petite",   33, 1, 1, 1, 0, 0 },\r
500   { "slim",     37, 2, 1, 0, 0, 0 },\r
501   { "small",    40, 2, 1, 0, 0, 0 },\r
502   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
503   { "middling", 49, 2, 0, 0, 0, 0 },\r
504   { "average",  54, 2, 0, 0, 0, 0 },\r
505   { "moderate", 58, 3, 0, 0, 0, 0 },\r
506   { "medium",   64, 3, 0, 0, 0, 0 },\r
507   { "bulky",    72, 3, 0, 0, 0, 0 },\r
508   { "large",    80, 3, 0, 0, 0, 0 },\r
509   { "big",      87, 3, 0, 0, 0, 0 },\r
510   { "huge",     95, 3, 0, 0, 0, 0 },\r
511   { "giant",    108, 3, 0, 0, 0, 0 },\r
512   { "colossal", 116, 4, 0, 0, 0, 0 },\r
513   { "titanic",  129, 4, 0, 0, 0, 0 },\r
514   { NULL, 0, 0, 0, 0, 0, 0 }\r
515 };\r
516 \r
517 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
518 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
519 {\r
520   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
521   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
522   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
523   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
524   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
525   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
526   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
527   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
528   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
529   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
530   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
531   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
532   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
533   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
534   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
535   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
536   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
537   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
538 };\r
539 \r
540 MyFont *font[NUM_SIZES][NUM_FONTS];\r
541 \r
542 typedef struct {\r
543   char *label;\r
544   int id;\r
545   HWND hwnd;\r
546   WNDPROC wndproc;\r
547 } MyButtonDesc;\r
548 \r
549 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
550 #define N_BUTTONS 5\r
551 \r
552 MyButtonDesc buttonDesc[N_BUTTONS] =\r
553 {\r
554   {"<<", IDM_ToStart, NULL, NULL},\r
555   {"<", IDM_Backward, NULL, NULL},\r
556   {"P", IDM_Pause, NULL, NULL},\r
557   {">", IDM_Forward, NULL, NULL},\r
558   {">>", IDM_ToEnd, NULL, NULL},\r
559 };\r
560 \r
561 int tinyLayout = 0, smallLayout = 0;\r
562 #define MENU_BAR_ITEMS 9\r
563 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
564   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
565   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
566 };\r
567 \r
568 \r
569 MySound sounds[(int)NSoundClasses];\r
570 MyTextAttribs textAttribs[(int)NColorClasses];\r
571 \r
572 MyColorizeAttribs colorizeAttribs[] = {\r
573   { (COLORREF)0, 0, N_("Shout Text") },\r
574   { (COLORREF)0, 0, N_("SShout/CShout") },\r
575   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
576   { (COLORREF)0, 0, N_("Channel Text") },\r
577   { (COLORREF)0, 0, N_("Kibitz Text") },\r
578   { (COLORREF)0, 0, N_("Tell Text") },\r
579   { (COLORREF)0, 0, N_("Challenge Text") },\r
580   { (COLORREF)0, 0, N_("Request Text") },\r
581   { (COLORREF)0, 0, N_("Seek Text") },\r
582   { (COLORREF)0, 0, N_("Normal Text") },\r
583   { (COLORREF)0, 0, N_("None") }\r
584 };\r
585 \r
586 \r
587 \r
588 static char *commentTitle;\r
589 static char *commentText;\r
590 static int commentIndex;\r
591 static Boolean editComment = FALSE;\r
592 \r
593 \r
594 char errorTitle[MSG_SIZ];\r
595 char errorMessage[2*MSG_SIZ];\r
596 HWND errorDialog = NULL;\r
597 BOOLEAN moveErrorMessageUp = FALSE;\r
598 BOOLEAN consoleEcho = TRUE;\r
599 CHARFORMAT consoleCF;\r
600 COLORREF consoleBackgroundColor;\r
601 \r
602 char *programVersion;\r
603 \r
604 #define CPReal 1\r
605 #define CPComm 2\r
606 #define CPSock 3\r
607 #define CPRcmd 4\r
608 typedef int CPKind;\r
609 \r
610 typedef struct {\r
611   CPKind kind;\r
612   HANDLE hProcess;\r
613   DWORD pid;\r
614   HANDLE hTo;\r
615   HANDLE hFrom;\r
616   SOCKET sock;\r
617   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
618 } ChildProc;\r
619 \r
620 #define INPUT_SOURCE_BUF_SIZE 4096\r
621 \r
622 typedef struct _InputSource {\r
623   CPKind kind;\r
624   HANDLE hFile;\r
625   SOCKET sock;\r
626   int lineByLine;\r
627   HANDLE hThread;\r
628   DWORD id;\r
629   char buf[INPUT_SOURCE_BUF_SIZE];\r
630   char *next;\r
631   DWORD count;\r
632   int error;\r
633   InputCallback func;\r
634   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
635   VOIDSTAR closure;\r
636 } InputSource;\r
637 \r
638 InputSource *consoleInputSource;\r
639 \r
640 DCB dcb;\r
641 \r
642 /* forward */\r
643 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
644 VOID ConsoleCreate();\r
645 LRESULT CALLBACK\r
646   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
647 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
648 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
649 VOID ParseCommSettings(char *arg, DCB *dcb);\r
650 LRESULT CALLBACK\r
651   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
652 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
653 void ParseIcsTextMenu(char *icsTextMenuString);\r
654 VOID PopUpMoveDialog(char firstchar);\r
655 VOID PopUpNameDialog(char firstchar);\r
656 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
657 \r
658 /* [AS] */\r
659 int NewGameFRC();\r
660 int GameListOptions();\r
661 \r
662 int dummy; // [HGM] for obsolete args\r
663 \r
664 HWND hwndMain = NULL;        /* root window*/\r
665 HWND hwndConsole = NULL;\r
666 HWND commentDialog = NULL;\r
667 HWND moveHistoryDialog = NULL;\r
668 HWND evalGraphDialog = NULL;\r
669 HWND engineOutputDialog = NULL;\r
670 HWND gameListDialog = NULL;\r
671 HWND editTagsDialog = NULL;\r
672 \r
673 int commentUp = FALSE;\r
674 \r
675 WindowPlacement wpMain;\r
676 WindowPlacement wpConsole;\r
677 WindowPlacement wpComment;\r
678 WindowPlacement wpMoveHistory;\r
679 WindowPlacement wpEvalGraph;\r
680 WindowPlacement wpEngineOutput;\r
681 WindowPlacement wpGameList;\r
682 WindowPlacement wpTags;\r
683 \r
684 VOID EngineOptionsPopup(); // [HGM] settings\r
685 \r
686 VOID GothicPopUp(char *title, VariantClass variant);\r
687 /*\r
688  * Setting "frozen" should disable all user input other than deleting\r
689  * the window.  We do this while engines are initializing themselves.\r
690  */\r
691 static int frozen = 0;\r
692 static int oldMenuItemState[MENU_BAR_ITEMS];\r
693 void FreezeUI()\r
694 {\r
695   HMENU hmenu;\r
696   int i;\r
697 \r
698   if (frozen) return;\r
699   frozen = 1;\r
700   hmenu = GetMenu(hwndMain);\r
701   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
702     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
703   }\r
704   DrawMenuBar(hwndMain);\r
705 }\r
706 \r
707 /* Undo a FreezeUI */\r
708 void ThawUI()\r
709 {\r
710   HMENU hmenu;\r
711   int i;\r
712 \r
713   if (!frozen) return;\r
714   frozen = 0;\r
715   hmenu = GetMenu(hwndMain);\r
716   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
717     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
718   }\r
719   DrawMenuBar(hwndMain);\r
720 }\r
721 \r
722 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
723 \r
724 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
725 #ifdef JAWS\r
726 #include "jaws.c"\r
727 #else\r
728 #define JAWS_INIT\r
729 #define JAWS_ARGS\r
730 #define JAWS_ALT_INTERCEPT\r
731 #define JAWS_KB_NAVIGATION\r
732 #define JAWS_MENU_ITEMS\r
733 #define JAWS_SILENCE\r
734 #define JAWS_REPLAY\r
735 #define JAWS_ACCEL\r
736 #define JAWS_COPYRIGHT\r
737 #define JAWS_DELETE(X) X\r
738 #define SAYMACHINEMOVE()\r
739 #define SAY(X)\r
740 #endif\r
741 \r
742 /*---------------------------------------------------------------------------*\\r
743  *\r
744  * WinMain\r
745  *\r
746 \*---------------------------------------------------------------------------*/\r
747 \r
748 int APIENTRY\r
749 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
750         LPSTR lpCmdLine, int nCmdShow)\r
751 {\r
752   MSG msg;\r
753   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
754 //  INITCOMMONCONTROLSEX ex;\r
755 \r
756   debugFP = stderr;\r
757 \r
758   LoadLibrary("RICHED32.DLL");\r
759   consoleCF.cbSize = sizeof(CHARFORMAT);\r
760 \r
761   if (!InitApplication(hInstance)) {\r
762     return (FALSE);\r
763   }\r
764   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
765     return (FALSE);\r
766   }\r
767 \r
768   JAWS_INIT\r
769   TranslateMenus(1);\r
770 \r
771 //  InitCommonControlsEx(&ex);\r
772   InitCommonControls();\r
773 \r
774   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
775   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
776   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
777 \r
778   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
779 \r
780   while (GetMessage(&msg, /* message structure */\r
781                     NULL, /* handle of window receiving the message */\r
782                     0,    /* lowest message to examine */\r
783                     0))   /* highest message to examine */\r
784     {\r
785 \r
786       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
787         // [HGM] navigate: switch between all windows with tab\r
788         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
789         int i, currentElement = 0;\r
790 \r
791         // first determine what element of the chain we come from (if any)\r
792         if(appData.icsActive) {\r
793             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
794             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
795         }\r
796         if(engineOutputDialog && EngineOutputIsUp()) {\r
797             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
798             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
799         }\r
800         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
801             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
802         }\r
803         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
804         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
805         if(msg.hwnd == e1)                 currentElement = 2; else\r
806         if(msg.hwnd == e2)                 currentElement = 3; else\r
807         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
808         if(msg.hwnd == mh)                currentElement = 4; else\r
809         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
810         if(msg.hwnd == hText)  currentElement = 5; else\r
811         if(msg.hwnd == hInput) currentElement = 6; else\r
812         for (i = 0; i < N_BUTTONS; i++) {\r
813             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
814         }\r
815 \r
816         // determine where to go to\r
817         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
818           do {\r
819             currentElement = (currentElement + direction) % 7;\r
820             switch(currentElement) {\r
821                 case 0:\r
822                   h = hwndMain; break; // passing this case always makes the loop exit\r
823                 case 1:\r
824                   h = buttonDesc[0].hwnd; break; // could be NULL\r
825                 case 2:\r
826                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
827                   h = e1; break;\r
828                 case 3:\r
829                   if(!EngineOutputIsUp()) continue;\r
830                   h = e2; break;\r
831                 case 4:\r
832                   if(!MoveHistoryIsUp()) continue;\r
833                   h = mh; break;\r
834 //              case 6: // input to eval graph does not seem to get here!\r
835 //                if(!EvalGraphIsUp()) continue;\r
836 //                h = evalGraphDialog; break;\r
837                 case 5:\r
838                   if(!appData.icsActive) continue;\r
839                   SAY("display");\r
840                   h = hText; break;\r
841                 case 6:\r
842                   if(!appData.icsActive) continue;\r
843                   SAY("input");\r
844                   h = hInput; break;\r
845             }\r
846           } while(h == 0);\r
847 \r
848           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
849           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
850           SetFocus(h);\r
851 \r
852           continue; // this message now has been processed\r
853         }\r
854       }\r
855 \r
856       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
857           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
858           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
859           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
860           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
861           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
862           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
863           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
864           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
865           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
866         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
867         for(i=0; i<MAX_CHAT; i++) \r
868             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
869                 done = 1; break;\r
870         }\r
871         if(done) continue; // [HGM] chat: end patch\r
872         TranslateMessage(&msg); /* Translates virtual key codes */\r
873         DispatchMessage(&msg);  /* Dispatches message to window */\r
874       }\r
875     }\r
876 \r
877 \r
878   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
879 }\r
880 \r
881 /*---------------------------------------------------------------------------*\\r
882  *\r
883  * Initialization functions\r
884  *\r
885 \*---------------------------------------------------------------------------*/\r
886 \r
887 void\r
888 SetUserLogo()\r
889 {   // update user logo if necessary\r
890     static char oldUserName[MSG_SIZ], *curName;\r
891 \r
892     if(appData.autoLogo) {\r
893           curName = UserName();\r
894           if(strcmp(curName, oldUserName)) {\r
895             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
896                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
897                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
898                 if(userLogo == NULL)\r
899                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
900           }\r
901     }\r
902 }\r
903 \r
904 BOOL\r
905 InitApplication(HINSTANCE hInstance)\r
906 {\r
907   WNDCLASS wc;\r
908 \r
909   /* Fill in window class structure with parameters that describe the */\r
910   /* main window. */\r
911 \r
912   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
913   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
914   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
915   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
916   wc.hInstance     = hInstance;         /* Owner of this class */\r
917   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
918   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
919   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
920   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
921   wc.lpszClassName = szAppName;                 /* Name to register as */\r
922 \r
923   /* Register the window class and return success/failure code. */\r
924   if (!RegisterClass(&wc)) return FALSE;\r
925 \r
926   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
927   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
928   wc.cbClsExtra    = 0;\r
929   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
930   wc.hInstance     = hInstance;\r
931   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
932   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
933   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
934   wc.lpszMenuName  = NULL;\r
935   wc.lpszClassName = szConsoleName;\r
936 \r
937   if (!RegisterClass(&wc)) return FALSE;\r
938   return TRUE;\r
939 }\r
940 \r
941 \r
942 /* Set by InitInstance, used by EnsureOnScreen */\r
943 int screenHeight, screenWidth;\r
944 \r
945 void\r
946 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
947 {\r
948 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
949   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
950   if (*x > screenWidth - 32) *x = 0;\r
951   if (*y > screenHeight - 32) *y = 0;\r
952   if (*x < minX) *x = minX;\r
953   if (*y < minY) *y = minY;\r
954 }\r
955 \r
956 BOOL\r
957 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
958 {\r
959   HWND hwnd; /* Main window handle. */\r
960   int ibs;\r
961   WINDOWPLACEMENT wp;\r
962   char *filepart;\r
963 \r
964   hInst = hInstance;    /* Store instance handle in our global variable */\r
965   programName = szAppName;\r
966 \r
967   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
968     *filepart = NULLCHAR;\r
969   } else {\r
970     GetCurrentDirectory(MSG_SIZ, installDir);\r
971   }\r
972   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
973   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
974   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
975   /* xboard, and older WinBoards, controlled the move sound with the\r
976      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
977      always turn the option on (so that the backend will call us),\r
978      then let the user turn the sound off by setting it to silence if\r
979      desired.  To accommodate old winboard.ini files saved by old\r
980      versions of WinBoard, we also turn off the sound if the option\r
981      was initially set to false. [HGM] taken out of InitAppData */\r
982   if (!appData.ringBellAfterMoves) {\r
983     sounds[(int)SoundMove].name = strdup("");\r
984     appData.ringBellAfterMoves = TRUE;\r
985   }\r
986   if (appData.debugMode) {\r
987     debugFP = fopen(appData.nameOfDebugFile, "w");\r
988     setbuf(debugFP, NULL);\r
989   }\r
990 \r
991   LoadLanguageFile(appData.language);\r
992 \r
993   InitBackEnd1();\r
994 \r
995 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
996 //  InitEngineUCI( installDir, &second );\r
997 \r
998   /* Create a main window for this application instance. */\r
999   hwnd = CreateWindow(szAppName, szTitle,\r
1000                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1001                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1002                       NULL, NULL, hInstance, NULL);\r
1003   hwndMain = hwnd;\r
1004 \r
1005   /* If window could not be created, return "failure" */\r
1006   if (!hwnd) {\r
1007     return (FALSE);\r
1008   }\r
1009 \r
1010   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1011   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
1012       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1013 \r
1014       if (first.programLogo == NULL && appData.debugMode) {\r
1015           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1016       }\r
1017   } else if(appData.autoLogo) {\r
1018       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1019         char buf[MSG_SIZ];\r
1020           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1021         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1022       }\r
1023   }\r
1024 \r
1025   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1026       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1027 \r
1028       if (second.programLogo == NULL && appData.debugMode) {\r
1029           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1030       }\r
1031   } else if(appData.autoLogo) {\r
1032       char buf[MSG_SIZ];\r
1033       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1034         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1035         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1036       } else\r
1037       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1038         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1039         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1040       }\r
1041   }\r
1042 \r
1043   SetUserLogo();\r
1044 \r
1045   iconWhite = LoadIcon(hInstance, "icon_white");\r
1046   iconBlack = LoadIcon(hInstance, "icon_black");\r
1047   iconCurrent = iconWhite;\r
1048   InitDrawingColors();\r
1049   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1050   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1051   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1052     /* Compute window size for each board size, and use the largest\r
1053        size that fits on this screen as the default. */\r
1054     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1055     if (boardSize == (BoardSize)-1 &&\r
1056         winH <= screenHeight\r
1057            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1058         && winW <= screenWidth) {\r
1059       boardSize = (BoardSize)ibs;\r
1060     }\r
1061   }\r
1062 \r
1063   InitDrawingSizes(boardSize, 0);\r
1064   InitMenuChecks();\r
1065   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1066 \r
1067   /* [AS] Load textures if specified */\r
1068   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1069   \r
1070   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1071       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1072       liteBackTextureMode = appData.liteBackTextureMode;\r
1073 \r
1074       if (liteBackTexture == NULL && appData.debugMode) {\r
1075           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1076       }\r
1077   }\r
1078   \r
1079   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1080       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1081       darkBackTextureMode = appData.darkBackTextureMode;\r
1082 \r
1083       if (darkBackTexture == NULL && appData.debugMode) {\r
1084           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1085       }\r
1086   }\r
1087 \r
1088   mysrandom( (unsigned) time(NULL) );\r
1089 \r
1090   /* [AS] Restore layout */\r
1091   if( wpMoveHistory.visible ) {\r
1092       MoveHistoryPopUp();\r
1093   }\r
1094 \r
1095   if( wpEvalGraph.visible ) {\r
1096       EvalGraphPopUp();\r
1097   }\r
1098 \r
1099   if( wpEngineOutput.visible ) {\r
1100       EngineOutputPopUp();\r
1101   }\r
1102 \r
1103   /* Make the window visible; update its client area; and return "success" */\r
1104   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1105   wp.length = sizeof(WINDOWPLACEMENT);\r
1106   wp.flags = 0;\r
1107   wp.showCmd = nCmdShow;\r
1108   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1109   wp.rcNormalPosition.left = wpMain.x;\r
1110   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1111   wp.rcNormalPosition.top = wpMain.y;\r
1112   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1113   SetWindowPlacement(hwndMain, &wp);\r
1114 \r
1115   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1116 \r
1117   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1118                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1119 \r
1120   if (hwndConsole) {\r
1121 #if AOT_CONSOLE\r
1122     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1123                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1124 #endif\r
1125     ShowWindow(hwndConsole, nCmdShow);\r
1126     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1127       char buf[MSG_SIZ], *p = buf, *q;\r
1128         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1129       do {\r
1130         q = strchr(p, ';');\r
1131         if(q) *q++ = 0;\r
1132         if(*p) ChatPopUp(p);\r
1133       } while(p=q);\r
1134     }\r
1135     SetActiveWindow(hwndConsole);\r
1136   }\r
1137   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1138   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1139 \r
1140   return TRUE;\r
1141 \r
1142 }\r
1143 \r
1144 VOID\r
1145 InitMenuChecks()\r
1146 {\r
1147   HMENU hmenu = GetMenu(hwndMain);\r
1148 \r
1149   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1150                         MF_BYCOMMAND|((appData.icsActive &&\r
1151                                        *appData.icsCommPort != NULLCHAR) ?\r
1152                                       MF_ENABLED : MF_GRAYED));\r
1153   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1154                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1155                                      MF_CHECKED : MF_UNCHECKED));\r
1156 }\r
1157 \r
1158 //---------------------------------------------------------------------------------------------------------\r
1159 \r
1160 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1161 #define XBOARD FALSE\r
1162 \r
1163 #define OPTCHAR "/"\r
1164 #define SEPCHAR "="\r
1165 \r
1166 #include "args.h"\r
1167 \r
1168 // front-end part of option handling\r
1169 \r
1170 VOID\r
1171 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1172 {\r
1173   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1174   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1175   DeleteDC(hdc);\r
1176   lf->lfWidth = 0;\r
1177   lf->lfEscapement = 0;\r
1178   lf->lfOrientation = 0;\r
1179   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1180   lf->lfItalic = mfp->italic;\r
1181   lf->lfUnderline = mfp->underline;\r
1182   lf->lfStrikeOut = mfp->strikeout;\r
1183   lf->lfCharSet = mfp->charset;\r
1184   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1185   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1186   lf->lfQuality = DEFAULT_QUALITY;\r
1187   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1188     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1189 }\r
1190 \r
1191 void\r
1192 CreateFontInMF(MyFont *mf)\r
1193\r
1194   LFfromMFP(&mf->lf, &mf->mfp);\r
1195   if (mf->hf) DeleteObject(mf->hf);\r
1196   mf->hf = CreateFontIndirect(&mf->lf);\r
1197 }\r
1198 \r
1199 // [HGM] This platform-dependent table provides the location for storing the color info\r
1200 void *\r
1201 colorVariable[] = {\r
1202   &whitePieceColor, \r
1203   &blackPieceColor, \r
1204   &lightSquareColor,\r
1205   &darkSquareColor, \r
1206   &highlightSquareColor,\r
1207   &premoveHighlightColor,\r
1208   NULL,\r
1209   &consoleBackgroundColor,\r
1210   &appData.fontForeColorWhite,\r
1211   &appData.fontBackColorWhite,\r
1212   &appData.fontForeColorBlack,\r
1213   &appData.fontBackColorBlack,\r
1214   &appData.evalHistColorWhite,\r
1215   &appData.evalHistColorBlack,\r
1216   &appData.highlightArrowColor,\r
1217 };\r
1218 \r
1219 /* Command line font name parser.  NULL name means do nothing.\r
1220    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1221    For backward compatibility, syntax without the colon is also\r
1222    accepted, but font names with digits in them won't work in that case.\r
1223 */\r
1224 VOID\r
1225 ParseFontName(char *name, MyFontParams *mfp)\r
1226 {\r
1227   char *p, *q;\r
1228   if (name == NULL) return;\r
1229   p = name;\r
1230   q = strchr(p, ':');\r
1231   if (q) {\r
1232     if (q - p >= sizeof(mfp->faceName))\r
1233       ExitArgError(_("Font name too long:"), name);\r
1234     memcpy(mfp->faceName, p, q - p);\r
1235     mfp->faceName[q - p] = NULLCHAR;\r
1236     p = q + 1;\r
1237   } else {\r
1238     q = mfp->faceName;\r
1239     while (*p && !isdigit(*p)) {\r
1240       *q++ = *p++;\r
1241       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1242         ExitArgError(_("Font name too long:"), name);\r
1243     }\r
1244     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1245     *q = NULLCHAR;\r
1246   }\r
1247   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1248   mfp->pointSize = (float) atof(p);\r
1249   mfp->bold = (strchr(p, 'b') != NULL);\r
1250   mfp->italic = (strchr(p, 'i') != NULL);\r
1251   mfp->underline = (strchr(p, 'u') != NULL);\r
1252   mfp->strikeout = (strchr(p, 's') != NULL);\r
1253   mfp->charset = DEFAULT_CHARSET;\r
1254   q = strchr(p, 'c');\r
1255   if (q)\r
1256     mfp->charset = (BYTE) atoi(q+1);\r
1257 }\r
1258 \r
1259 void\r
1260 ParseFont(char *name, int number)\r
1261 { // wrapper to shield back-end from 'font'\r
1262   ParseFontName(name, &font[boardSize][number]->mfp);\r
1263 }\r
1264 \r
1265 void\r
1266 SetFontDefaults()\r
1267 { // in WB  we have a 2D array of fonts; this initializes their description\r
1268   int i, j;\r
1269   /* Point font array elements to structures and\r
1270      parse default font names */\r
1271   for (i=0; i<NUM_FONTS; i++) {\r
1272     for (j=0; j<NUM_SIZES; j++) {\r
1273       font[j][i] = &fontRec[j][i];\r
1274       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1275     }\r
1276   }\r
1277 }\r
1278 \r
1279 void\r
1280 CreateFonts()\r
1281 { // here we create the actual fonts from the selected descriptions\r
1282   int i, j;\r
1283   for (i=0; i<NUM_FONTS; i++) {\r
1284     for (j=0; j<NUM_SIZES; j++) {\r
1285       CreateFontInMF(font[j][i]);\r
1286     }\r
1287   }\r
1288 }\r
1289 /* Color name parser.\r
1290    X version accepts X color names, but this one\r
1291    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1292 COLORREF\r
1293 ParseColorName(char *name)\r
1294 {\r
1295   int red, green, blue, count;\r
1296   char buf[MSG_SIZ];\r
1297 \r
1298   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1299   if (count != 3) {\r
1300     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1301       &red, &green, &blue);\r
1302   }\r
1303   if (count != 3) {\r
1304     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1305     DisplayError(buf, 0);\r
1306     return RGB(0, 0, 0);\r
1307   }\r
1308   return PALETTERGB(red, green, blue);\r
1309 }\r
1310 \r
1311 void\r
1312 ParseColor(int n, char *name)\r
1313 { // for WinBoard the color is an int, which needs to be derived from the string\r
1314   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1315 }\r
1316 \r
1317 void\r
1318 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1319 {\r
1320   char *e = argValue;\r
1321   int eff = 0;\r
1322 \r
1323   while (*e) {\r
1324     if (*e == 'b')      eff |= CFE_BOLD;\r
1325     else if (*e == 'i') eff |= CFE_ITALIC;\r
1326     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1327     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1328     else if (*e == '#' || isdigit(*e)) break;\r
1329     e++;\r
1330   }\r
1331   *effects = eff;\r
1332   *color   = ParseColorName(e);\r
1333 }\r
1334 \r
1335 void\r
1336 ParseTextAttribs(ColorClass cc, char *s)\r
1337 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1338     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1339     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1340 }\r
1341 \r
1342 void\r
1343 ParseBoardSize(void *addr, char *name)\r
1344 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1345   BoardSize bs = SizeTiny;\r
1346   while (sizeInfo[bs].name != NULL) {\r
1347     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1348         *(BoardSize *)addr = bs;\r
1349         return;\r
1350     }\r
1351     bs++;\r
1352   }\r
1353   ExitArgError(_("Unrecognized board size value"), name);\r
1354 }\r
1355 \r
1356 void\r
1357 LoadAllSounds()\r
1358 { // [HGM] import name from appData first\r
1359   ColorClass cc;\r
1360   SoundClass sc;\r
1361   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1362     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1363     textAttribs[cc].sound.data = NULL;\r
1364     MyLoadSound(&textAttribs[cc].sound);\r
1365   }\r
1366   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1367     textAttribs[cc].sound.name = strdup("");\r
1368     textAttribs[cc].sound.data = NULL;\r
1369   }\r
1370   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1371     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1372     sounds[sc].data = NULL;\r
1373     MyLoadSound(&sounds[sc]);\r
1374   }\r
1375 }\r
1376 \r
1377 void\r
1378 SetCommPortDefaults()\r
1379 {\r
1380    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1381   dcb.DCBlength = sizeof(DCB);\r
1382   dcb.BaudRate = 9600;\r
1383   dcb.fBinary = TRUE;\r
1384   dcb.fParity = FALSE;\r
1385   dcb.fOutxCtsFlow = FALSE;\r
1386   dcb.fOutxDsrFlow = FALSE;\r
1387   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1388   dcb.fDsrSensitivity = FALSE;\r
1389   dcb.fTXContinueOnXoff = TRUE;\r
1390   dcb.fOutX = FALSE;\r
1391   dcb.fInX = FALSE;\r
1392   dcb.fNull = FALSE;\r
1393   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1394   dcb.fAbortOnError = FALSE;\r
1395   dcb.ByteSize = 7;\r
1396   dcb.Parity = SPACEPARITY;\r
1397   dcb.StopBits = ONESTOPBIT;\r
1398 }\r
1399 \r
1400 // [HGM] args: these three cases taken out to stay in front-end\r
1401 void\r
1402 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1403 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1404         // while the curent board size determines the element. This system should be ported to XBoard.\r
1405         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1406         int bs;\r
1407         for (bs=0; bs<NUM_SIZES; bs++) {\r
1408           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1409           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1410           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1411             ad->argName, mfp->faceName, mfp->pointSize,\r
1412             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1413             mfp->bold ? "b" : "",\r
1414             mfp->italic ? "i" : "",\r
1415             mfp->underline ? "u" : "",\r
1416             mfp->strikeout ? "s" : "",\r
1417             (int)mfp->charset);\r
1418         }\r
1419       }\r
1420 \r
1421 void\r
1422 ExportSounds()\r
1423 { // [HGM] copy the names from the internal WB variables to appData\r
1424   ColorClass cc;\r
1425   SoundClass sc;\r
1426   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1427     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1428   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1429     (&appData.soundMove)[sc] = sounds[sc].name;\r
1430 }\r
1431 \r
1432 void\r
1433 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1434 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1435         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1436         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1437           (ta->effects & CFE_BOLD) ? "b" : "",\r
1438           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1439           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1440           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1441           (ta->effects) ? " " : "",\r
1442           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1443       }\r
1444 \r
1445 void\r
1446 SaveColor(FILE *f, ArgDescriptor *ad)\r
1447 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1448         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1449         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1450           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1451 }\r
1452 \r
1453 void\r
1454 SaveBoardSize(FILE *f, char *name, void *addr)\r
1455 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1456   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1457 }\r
1458 \r
1459 void\r
1460 ParseCommPortSettings(char *s)\r
1461 { // wrapper to keep dcb from back-end\r
1462   ParseCommSettings(s, &dcb);\r
1463 }\r
1464 \r
1465 void\r
1466 GetWindowCoords()\r
1467 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1468   GetActualPlacement(hwndMain, &wpMain);\r
1469   GetActualPlacement(hwndConsole, &wpConsole);\r
1470   GetActualPlacement(commentDialog, &wpComment);\r
1471   GetActualPlacement(editTagsDialog, &wpTags);\r
1472   GetActualPlacement(gameListDialog, &wpGameList);\r
1473   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1474   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1475   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1476 }\r
1477 \r
1478 void\r
1479 PrintCommPortSettings(FILE *f, char *name)\r
1480 { // wrapper to shield back-end from DCB\r
1481       PrintCommSettings(f, name, &dcb);\r
1482 }\r
1483 \r
1484 int\r
1485 MySearchPath(char *installDir, char *name, char *fullname)\r
1486 {\r
1487   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1488   if(name[0]== '%') {\r
1489     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1490     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1491       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1492       *strchr(buf, '%') = 0;\r
1493       strcat(fullname, getenv(buf));\r
1494       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1495     }\r
1496     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1497     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1498     return (int) strlen(fullname);\r
1499   }\r
1500   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1501 }\r
1502 \r
1503 int\r
1504 MyGetFullPathName(char *name, char *fullname)\r
1505 {\r
1506   char *dummy;\r
1507   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1508 }\r
1509 \r
1510 int\r
1511 MainWindowUp()\r
1512 { // [HGM] args: allows testing if main window is realized from back-end\r
1513   return hwndMain != NULL;\r
1514 }\r
1515 \r
1516 void\r
1517 PopUpStartupDialog()\r
1518 {\r
1519     FARPROC lpProc;\r
1520     \r
1521     LoadLanguageFile(appData.language);\r
1522     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1523     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1524     FreeProcInstance(lpProc);\r
1525 }\r
1526 \r
1527 /*---------------------------------------------------------------------------*\\r
1528  *\r
1529  * GDI board drawing routines\r
1530  *\r
1531 \*---------------------------------------------------------------------------*/\r
1532 \r
1533 /* [AS] Draw square using background texture */\r
1534 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1535 {\r
1536     XFORM   x;\r
1537 \r
1538     if( mode == 0 ) {\r
1539         return; /* Should never happen! */\r
1540     }\r
1541 \r
1542     SetGraphicsMode( dst, GM_ADVANCED );\r
1543 \r
1544     switch( mode ) {\r
1545     case 1:\r
1546         /* Identity */\r
1547         break;\r
1548     case 2:\r
1549         /* X reflection */\r
1550         x.eM11 = -1.0;\r
1551         x.eM12 = 0;\r
1552         x.eM21 = 0;\r
1553         x.eM22 = 1.0;\r
1554         x.eDx = (FLOAT) dw + dx - 1;\r
1555         x.eDy = 0;\r
1556         dx = 0;\r
1557         SetWorldTransform( dst, &x );\r
1558         break;\r
1559     case 3:\r
1560         /* Y reflection */\r
1561         x.eM11 = 1.0;\r
1562         x.eM12 = 0;\r
1563         x.eM21 = 0;\r
1564         x.eM22 = -1.0;\r
1565         x.eDx = 0;\r
1566         x.eDy = (FLOAT) dh + dy - 1;\r
1567         dy = 0;\r
1568         SetWorldTransform( dst, &x );\r
1569         break;\r
1570     case 4:\r
1571         /* X/Y flip */\r
1572         x.eM11 = 0;\r
1573         x.eM12 = 1.0;\r
1574         x.eM21 = 1.0;\r
1575         x.eM22 = 0;\r
1576         x.eDx = (FLOAT) dx;\r
1577         x.eDy = (FLOAT) dy;\r
1578         dx = 0;\r
1579         dy = 0;\r
1580         SetWorldTransform( dst, &x );\r
1581         break;\r
1582     }\r
1583 \r
1584     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1585 \r
1586     x.eM11 = 1.0;\r
1587     x.eM12 = 0;\r
1588     x.eM21 = 0;\r
1589     x.eM22 = 1.0;\r
1590     x.eDx = 0;\r
1591     x.eDy = 0;\r
1592     SetWorldTransform( dst, &x );\r
1593 \r
1594     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1595 }\r
1596 \r
1597 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1598 enum {\r
1599     PM_WP = (int) WhitePawn, \r
1600     PM_WN = (int) WhiteKnight, \r
1601     PM_WB = (int) WhiteBishop, \r
1602     PM_WR = (int) WhiteRook, \r
1603     PM_WQ = (int) WhiteQueen, \r
1604     PM_WF = (int) WhiteFerz, \r
1605     PM_WW = (int) WhiteWazir, \r
1606     PM_WE = (int) WhiteAlfil, \r
1607     PM_WM = (int) WhiteMan, \r
1608     PM_WO = (int) WhiteCannon, \r
1609     PM_WU = (int) WhiteUnicorn, \r
1610     PM_WH = (int) WhiteNightrider, \r
1611     PM_WA = (int) WhiteAngel, \r
1612     PM_WC = (int) WhiteMarshall, \r
1613     PM_WAB = (int) WhiteCardinal, \r
1614     PM_WD = (int) WhiteDragon, \r
1615     PM_WL = (int) WhiteLance, \r
1616     PM_WS = (int) WhiteCobra, \r
1617     PM_WV = (int) WhiteFalcon, \r
1618     PM_WSG = (int) WhiteSilver, \r
1619     PM_WG = (int) WhiteGrasshopper, \r
1620     PM_WK = (int) WhiteKing,\r
1621     PM_BP = (int) BlackPawn, \r
1622     PM_BN = (int) BlackKnight, \r
1623     PM_BB = (int) BlackBishop, \r
1624     PM_BR = (int) BlackRook, \r
1625     PM_BQ = (int) BlackQueen, \r
1626     PM_BF = (int) BlackFerz, \r
1627     PM_BW = (int) BlackWazir, \r
1628     PM_BE = (int) BlackAlfil, \r
1629     PM_BM = (int) BlackMan,\r
1630     PM_BO = (int) BlackCannon, \r
1631     PM_BU = (int) BlackUnicorn, \r
1632     PM_BH = (int) BlackNightrider, \r
1633     PM_BA = (int) BlackAngel, \r
1634     PM_BC = (int) BlackMarshall, \r
1635     PM_BG = (int) BlackGrasshopper, \r
1636     PM_BAB = (int) BlackCardinal,\r
1637     PM_BD = (int) BlackDragon,\r
1638     PM_BL = (int) BlackLance,\r
1639     PM_BS = (int) BlackCobra,\r
1640     PM_BV = (int) BlackFalcon,\r
1641     PM_BSG = (int) BlackSilver,\r
1642     PM_BK = (int) BlackKing\r
1643 };\r
1644 \r
1645 static HFONT hPieceFont = NULL;\r
1646 static HBITMAP hPieceMask[(int) EmptySquare];\r
1647 static HBITMAP hPieceFace[(int) EmptySquare];\r
1648 static int fontBitmapSquareSize = 0;\r
1649 static char pieceToFontChar[(int) EmptySquare] =\r
1650                               { 'p', 'n', 'b', 'r', 'q', \r
1651                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1652                       'k', 'o', 'm', 'v', 't', 'w', \r
1653                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1654                                                               'l' };\r
1655 \r
1656 extern BOOL SetCharTable( char *table, const char * map );\r
1657 /* [HGM] moved to backend.c */\r
1658 \r
1659 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1660 {\r
1661     HBRUSH hbrush;\r
1662     BYTE r1 = GetRValue( color );\r
1663     BYTE g1 = GetGValue( color );\r
1664     BYTE b1 = GetBValue( color );\r
1665     BYTE r2 = r1 / 2;\r
1666     BYTE g2 = g1 / 2;\r
1667     BYTE b2 = b1 / 2;\r
1668     RECT rc;\r
1669 \r
1670     /* Create a uniform background first */\r
1671     hbrush = CreateSolidBrush( color );\r
1672     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1673     FillRect( hdc, &rc, hbrush );\r
1674     DeleteObject( hbrush );\r
1675     \r
1676     if( mode == 1 ) {\r
1677         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1678         int steps = squareSize / 2;\r
1679         int i;\r
1680 \r
1681         for( i=0; i<steps; i++ ) {\r
1682             BYTE r = r1 - (r1-r2) * i / steps;\r
1683             BYTE g = g1 - (g1-g2) * i / steps;\r
1684             BYTE b = b1 - (b1-b2) * i / steps;\r
1685 \r
1686             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1687             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1688             FillRect( hdc, &rc, hbrush );\r
1689             DeleteObject(hbrush);\r
1690         }\r
1691     }\r
1692     else if( mode == 2 ) {\r
1693         /* Diagonal gradient, good more or less for every piece */\r
1694         POINT triangle[3];\r
1695         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1696         HBRUSH hbrush_old;\r
1697         int steps = squareSize;\r
1698         int i;\r
1699 \r
1700         triangle[0].x = squareSize - steps;\r
1701         triangle[0].y = squareSize;\r
1702         triangle[1].x = squareSize;\r
1703         triangle[1].y = squareSize;\r
1704         triangle[2].x = squareSize;\r
1705         triangle[2].y = squareSize - steps;\r
1706 \r
1707         for( i=0; i<steps; i++ ) {\r
1708             BYTE r = r1 - (r1-r2) * i / steps;\r
1709             BYTE g = g1 - (g1-g2) * i / steps;\r
1710             BYTE b = b1 - (b1-b2) * i / steps;\r
1711 \r
1712             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1713             hbrush_old = SelectObject( hdc, hbrush );\r
1714             Polygon( hdc, triangle, 3 );\r
1715             SelectObject( hdc, hbrush_old );\r
1716             DeleteObject(hbrush);\r
1717             triangle[0].x++;\r
1718             triangle[2].y++;\r
1719         }\r
1720 \r
1721         SelectObject( hdc, hpen );\r
1722     }\r
1723 }\r
1724 \r
1725 /*\r
1726     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1727     seems to work ok. The main problem here is to find the "inside" of a chess\r
1728     piece: follow the steps as explained below.\r
1729 */\r
1730 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1731 {\r
1732     HBITMAP hbm;\r
1733     HBITMAP hbm_old;\r
1734     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1735     RECT rc;\r
1736     SIZE sz;\r
1737     POINT pt;\r
1738     int backColor = whitePieceColor; \r
1739     int foreColor = blackPieceColor;\r
1740     \r
1741     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1742         backColor = appData.fontBackColorWhite;\r
1743         foreColor = appData.fontForeColorWhite;\r
1744     }\r
1745     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1746         backColor = appData.fontBackColorBlack;\r
1747         foreColor = appData.fontForeColorBlack;\r
1748     }\r
1749 \r
1750     /* Mask */\r
1751     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1752 \r
1753     hbm_old = SelectObject( hdc, hbm );\r
1754 \r
1755     rc.left = 0;\r
1756     rc.top = 0;\r
1757     rc.right = squareSize;\r
1758     rc.bottom = squareSize;\r
1759 \r
1760     /* Step 1: background is now black */\r
1761     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1762 \r
1763     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1764 \r
1765     pt.x = (squareSize - sz.cx) / 2;\r
1766     pt.y = (squareSize - sz.cy) / 2;\r
1767 \r
1768     SetBkMode( hdc, TRANSPARENT );\r
1769     SetTextColor( hdc, chroma );\r
1770     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1771     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1772 \r
1773     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1774     /* Step 3: the area outside the piece is filled with white */\r
1775 //    FloodFill( hdc, 0, 0, chroma );\r
1776     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1777     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1778     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1779     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1780     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1781     /* \r
1782         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1783         but if the start point is not inside the piece we're lost!\r
1784         There should be a better way to do this... if we could create a region or path\r
1785         from the fill operation we would be fine for example.\r
1786     */\r
1787 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1788     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1789 \r
1790     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1791         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1792         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1793 \r
1794         SelectObject( dc2, bm2 );\r
1795         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1796         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1797         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1798         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1799         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1800 \r
1801         DeleteDC( dc2 );\r
1802         DeleteObject( bm2 );\r
1803     }\r
1804 \r
1805     SetTextColor( hdc, 0 );\r
1806     /* \r
1807         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1808         draw the piece again in black for safety.\r
1809     */\r
1810     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1811 \r
1812     SelectObject( hdc, hbm_old );\r
1813 \r
1814     if( hPieceMask[index] != NULL ) {\r
1815         DeleteObject( hPieceMask[index] );\r
1816     }\r
1817 \r
1818     hPieceMask[index] = hbm;\r
1819 \r
1820     /* Face */\r
1821     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1822 \r
1823     SelectObject( hdc, hbm );\r
1824 \r
1825     {\r
1826         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1827         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1828         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1829 \r
1830         SelectObject( dc1, hPieceMask[index] );\r
1831         SelectObject( dc2, bm2 );\r
1832         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1833         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1834         \r
1835         /* \r
1836             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1837             the piece background and deletes (makes transparent) the rest.\r
1838             Thanks to that mask, we are free to paint the background with the greates\r
1839             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1840             We use this, to make gradients and give the pieces a "roundish" look.\r
1841         */\r
1842         SetPieceBackground( hdc, backColor, 2 );\r
1843         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1844 \r
1845         DeleteDC( dc2 );\r
1846         DeleteDC( dc1 );\r
1847         DeleteObject( bm2 );\r
1848     }\r
1849 \r
1850     SetTextColor( hdc, foreColor );\r
1851     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1852 \r
1853     SelectObject( hdc, hbm_old );\r
1854 \r
1855     if( hPieceFace[index] != NULL ) {\r
1856         DeleteObject( hPieceFace[index] );\r
1857     }\r
1858 \r
1859     hPieceFace[index] = hbm;\r
1860 }\r
1861 \r
1862 static int TranslatePieceToFontPiece( int piece )\r
1863 {\r
1864     switch( piece ) {\r
1865     case BlackPawn:\r
1866         return PM_BP;\r
1867     case BlackKnight:\r
1868         return PM_BN;\r
1869     case BlackBishop:\r
1870         return PM_BB;\r
1871     case BlackRook:\r
1872         return PM_BR;\r
1873     case BlackQueen:\r
1874         return PM_BQ;\r
1875     case BlackKing:\r
1876         return PM_BK;\r
1877     case WhitePawn:\r
1878         return PM_WP;\r
1879     case WhiteKnight:\r
1880         return PM_WN;\r
1881     case WhiteBishop:\r
1882         return PM_WB;\r
1883     case WhiteRook:\r
1884         return PM_WR;\r
1885     case WhiteQueen:\r
1886         return PM_WQ;\r
1887     case WhiteKing:\r
1888         return PM_WK;\r
1889 \r
1890     case BlackAngel:\r
1891         return PM_BA;\r
1892     case BlackMarshall:\r
1893         return PM_BC;\r
1894     case BlackFerz:\r
1895         return PM_BF;\r
1896     case BlackNightrider:\r
1897         return PM_BH;\r
1898     case BlackAlfil:\r
1899         return PM_BE;\r
1900     case BlackWazir:\r
1901         return PM_BW;\r
1902     case BlackUnicorn:\r
1903         return PM_BU;\r
1904     case BlackCannon:\r
1905         return PM_BO;\r
1906     case BlackGrasshopper:\r
1907         return PM_BG;\r
1908     case BlackMan:\r
1909         return PM_BM;\r
1910     case BlackSilver:\r
1911         return PM_BSG;\r
1912     case BlackLance:\r
1913         return PM_BL;\r
1914     case BlackFalcon:\r
1915         return PM_BV;\r
1916     case BlackCobra:\r
1917         return PM_BS;\r
1918     case BlackCardinal:\r
1919         return PM_BAB;\r
1920     case BlackDragon:\r
1921         return PM_BD;\r
1922 \r
1923     case WhiteAngel:\r
1924         return PM_WA;\r
1925     case WhiteMarshall:\r
1926         return PM_WC;\r
1927     case WhiteFerz:\r
1928         return PM_WF;\r
1929     case WhiteNightrider:\r
1930         return PM_WH;\r
1931     case WhiteAlfil:\r
1932         return PM_WE;\r
1933     case WhiteWazir:\r
1934         return PM_WW;\r
1935     case WhiteUnicorn:\r
1936         return PM_WU;\r
1937     case WhiteCannon:\r
1938         return PM_WO;\r
1939     case WhiteGrasshopper:\r
1940         return PM_WG;\r
1941     case WhiteMan:\r
1942         return PM_WM;\r
1943     case WhiteSilver:\r
1944         return PM_WSG;\r
1945     case WhiteLance:\r
1946         return PM_WL;\r
1947     case WhiteFalcon:\r
1948         return PM_WV;\r
1949     case WhiteCobra:\r
1950         return PM_WS;\r
1951     case WhiteCardinal:\r
1952         return PM_WAB;\r
1953     case WhiteDragon:\r
1954         return PM_WD;\r
1955     }\r
1956 \r
1957     return 0;\r
1958 }\r
1959 \r
1960 void CreatePiecesFromFont()\r
1961 {\r
1962     LOGFONT lf;\r
1963     HDC hdc_window = NULL;\r
1964     HDC hdc = NULL;\r
1965     HFONT hfont_old;\r
1966     int fontHeight;\r
1967     int i;\r
1968 \r
1969     if( fontBitmapSquareSize < 0 ) {\r
1970         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1971         return;\r
1972     }\r
1973 \r
1974     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1975         fontBitmapSquareSize = -1;\r
1976         return;\r
1977     }\r
1978 \r
1979     if( fontBitmapSquareSize != squareSize ) {\r
1980         hdc_window = GetDC( hwndMain );\r
1981         hdc = CreateCompatibleDC( hdc_window );\r
1982 \r
1983         if( hPieceFont != NULL ) {\r
1984             DeleteObject( hPieceFont );\r
1985         }\r
1986         else {\r
1987             for( i=0; i<=(int)BlackKing; i++ ) {\r
1988                 hPieceMask[i] = NULL;\r
1989                 hPieceFace[i] = NULL;\r
1990             }\r
1991         }\r
1992 \r
1993         fontHeight = 75;\r
1994 \r
1995         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1996             fontHeight = appData.fontPieceSize;\r
1997         }\r
1998 \r
1999         fontHeight = (fontHeight * squareSize) / 100;\r
2000 \r
2001         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2002         lf.lfWidth = 0;\r
2003         lf.lfEscapement = 0;\r
2004         lf.lfOrientation = 0;\r
2005         lf.lfWeight = FW_NORMAL;\r
2006         lf.lfItalic = 0;\r
2007         lf.lfUnderline = 0;\r
2008         lf.lfStrikeOut = 0;\r
2009         lf.lfCharSet = DEFAULT_CHARSET;\r
2010         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2011         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2012         lf.lfQuality = PROOF_QUALITY;\r
2013         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2014         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2015         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2016 \r
2017         hPieceFont = CreateFontIndirect( &lf );\r
2018 \r
2019         if( hPieceFont == NULL ) {\r
2020             fontBitmapSquareSize = -2;\r
2021         }\r
2022         else {\r
2023             /* Setup font-to-piece character table */\r
2024             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2025                 /* No (or wrong) global settings, try to detect the font */\r
2026                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2027                     /* Alpha */\r
2028                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2029                 }\r
2030                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2031                     /* DiagramTT* family */\r
2032                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2033                 }\r
2034                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2035                     /* Fairy symbols */\r
2036                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2037                 }\r
2038                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2039                     /* Good Companion (Some characters get warped as literal :-( */\r
2040                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2041                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2042                     SetCharTable(pieceToFontChar, s);\r
2043                 }\r
2044                 else {\r
2045                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2046                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2047                 }\r
2048             }\r
2049 \r
2050             /* Create bitmaps */\r
2051             hfont_old = SelectObject( hdc, hPieceFont );\r
2052             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2053                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2054                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2055 \r
2056             SelectObject( hdc, hfont_old );\r
2057 \r
2058             fontBitmapSquareSize = squareSize;\r
2059         }\r
2060     }\r
2061 \r
2062     if( hdc != NULL ) {\r
2063         DeleteDC( hdc );\r
2064     }\r
2065 \r
2066     if( hdc_window != NULL ) {\r
2067         ReleaseDC( hwndMain, hdc_window );\r
2068     }\r
2069 }\r
2070 \r
2071 HBITMAP\r
2072 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2073 {\r
2074   char name[128];\r
2075 \r
2076     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2077   if (gameInfo.event &&\r
2078       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2079       strcmp(name, "k80s") == 0) {\r
2080     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2081   }\r
2082   return LoadBitmap(hinst, name);\r
2083 }\r
2084 \r
2085 \r
2086 /* Insert a color into the program's logical palette\r
2087    structure.  This code assumes the given color is\r
2088    the result of the RGB or PALETTERGB macro, and it\r
2089    knows how those macros work (which is documented).\r
2090 */\r
2091 VOID\r
2092 InsertInPalette(COLORREF color)\r
2093 {\r
2094   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2095 \r
2096   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2097     DisplayFatalError(_("Too many colors"), 0, 1);\r
2098     pLogPal->palNumEntries--;\r
2099     return;\r
2100   }\r
2101 \r
2102   pe->peFlags = (char) 0;\r
2103   pe->peRed = (char) (0xFF & color);\r
2104   pe->peGreen = (char) (0xFF & (color >> 8));\r
2105   pe->peBlue = (char) (0xFF & (color >> 16));\r
2106   return;\r
2107 }\r
2108 \r
2109 \r
2110 VOID\r
2111 InitDrawingColors()\r
2112 {\r
2113   if (pLogPal == NULL) {\r
2114     /* Allocate enough memory for a logical palette with\r
2115      * PALETTESIZE entries and set the size and version fields\r
2116      * of the logical palette structure.\r
2117      */\r
2118     pLogPal = (NPLOGPALETTE)\r
2119       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2120                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2121     pLogPal->palVersion    = 0x300;\r
2122   }\r
2123   pLogPal->palNumEntries = 0;\r
2124 \r
2125   InsertInPalette(lightSquareColor);\r
2126   InsertInPalette(darkSquareColor);\r
2127   InsertInPalette(whitePieceColor);\r
2128   InsertInPalette(blackPieceColor);\r
2129   InsertInPalette(highlightSquareColor);\r
2130   InsertInPalette(premoveHighlightColor);\r
2131 \r
2132   /*  create a logical color palette according the information\r
2133    *  in the LOGPALETTE structure.\r
2134    */\r
2135   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2136 \r
2137   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2138   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2139   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2140   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2141   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2142   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2143   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2144   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2145   /* [AS] Force rendering of the font-based pieces */\r
2146   if( fontBitmapSquareSize > 0 ) {\r
2147     fontBitmapSquareSize = 0;\r
2148   }\r
2149 }\r
2150 \r
2151 \r
2152 int\r
2153 BoardWidth(int boardSize, int n)\r
2154 { /* [HGM] argument n added to allow different width and height */\r
2155   int lineGap = sizeInfo[boardSize].lineGap;\r
2156 \r
2157   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2158       lineGap = appData.overrideLineGap;\r
2159   }\r
2160 \r
2161   return (n + 1) * lineGap +\r
2162           n * sizeInfo[boardSize].squareSize;\r
2163 }\r
2164 \r
2165 /* Respond to board resize by dragging edge */\r
2166 VOID\r
2167 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2168 {\r
2169   BoardSize newSize = NUM_SIZES - 1;\r
2170   static int recurse = 0;\r
2171   if (IsIconic(hwndMain)) return;\r
2172   if (recurse > 0) return;\r
2173   recurse++;\r
2174   while (newSize > 0) {\r
2175         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2176         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2177            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2178     newSize--;\r
2179   } \r
2180   boardSize = newSize;\r
2181   InitDrawingSizes(boardSize, flags);\r
2182   recurse--;\r
2183 }\r
2184 \r
2185 \r
2186 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2187 \r
2188 VOID\r
2189 InitDrawingSizes(BoardSize boardSize, int flags)\r
2190 {\r
2191   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2192   ChessSquare piece;\r
2193   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2194   HDC hdc;\r
2195   SIZE clockSize, messageSize;\r
2196   HFONT oldFont;\r
2197   char buf[MSG_SIZ];\r
2198   char *str;\r
2199   HMENU hmenu = GetMenu(hwndMain);\r
2200   RECT crect, wrect, oldRect;\r
2201   int offby;\r
2202   LOGBRUSH logbrush;\r
2203 \r
2204   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2205   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2206 \r
2207   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2208   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2209 \r
2210   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2211   oldRect.top = wpMain.y;\r
2212   oldRect.right = wpMain.x + wpMain.width;\r
2213   oldRect.bottom = wpMain.y + wpMain.height;\r
2214 \r
2215   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2216   smallLayout = sizeInfo[boardSize].smallLayout;\r
2217   squareSize = sizeInfo[boardSize].squareSize;\r
2218   lineGap = sizeInfo[boardSize].lineGap;\r
2219   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2220 \r
2221   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2222       lineGap = appData.overrideLineGap;\r
2223   }\r
2224 \r
2225   if (tinyLayout != oldTinyLayout) {\r
2226     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2227     if (tinyLayout) {\r
2228       style &= ~WS_SYSMENU;\r
2229       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2230                  "&Minimize\tCtrl+F4");\r
2231     } else {\r
2232       style |= WS_SYSMENU;\r
2233       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2234     }\r
2235     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2236 \r
2237     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2238       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2239         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2240     }\r
2241     DrawMenuBar(hwndMain);\r
2242   }\r
2243 \r
2244   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2245   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2246 \r
2247   /* Get text area sizes */\r
2248   hdc = GetDC(hwndMain);\r
2249   if (appData.clockMode) {\r
2250     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2251   } else {\r
2252     snprintf(buf, MSG_SIZ, _("White"));\r
2253   }\r
2254   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2255   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2256   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2257   str = _("We only care about the height here");\r
2258   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2259   SelectObject(hdc, oldFont);\r
2260   ReleaseDC(hwndMain, hdc);\r
2261 \r
2262   /* Compute where everything goes */\r
2263   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2264         /* [HGM] logo: if either logo is on, reserve space for it */\r
2265         logoHeight =  2*clockSize.cy;\r
2266         leftLogoRect.left   = OUTER_MARGIN;\r
2267         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2268         leftLogoRect.top    = OUTER_MARGIN;\r
2269         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2270 \r
2271         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2272         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2273         rightLogoRect.top    = OUTER_MARGIN;\r
2274         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2275 \r
2276 \r
2277     whiteRect.left = leftLogoRect.right;\r
2278     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2279     whiteRect.top = OUTER_MARGIN;\r
2280     whiteRect.bottom = whiteRect.top + logoHeight;\r
2281 \r
2282     blackRect.right = rightLogoRect.left;\r
2283     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2284     blackRect.top = whiteRect.top;\r
2285     blackRect.bottom = whiteRect.bottom;\r
2286   } else {\r
2287     whiteRect.left = OUTER_MARGIN;\r
2288     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2289     whiteRect.top = OUTER_MARGIN;\r
2290     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2291 \r
2292     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2293     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2294     blackRect.top = whiteRect.top;\r
2295     blackRect.bottom = whiteRect.bottom;\r
2296 \r
2297     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2298   }\r
2299 \r
2300   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2301   if (appData.showButtonBar) {\r
2302     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2303       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2304   } else {\r
2305     messageRect.right = OUTER_MARGIN + boardWidth;\r
2306   }\r
2307   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2308   messageRect.bottom = messageRect.top + messageSize.cy;\r
2309 \r
2310   boardRect.left = OUTER_MARGIN;\r
2311   boardRect.right = boardRect.left + boardWidth;\r
2312   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2313   boardRect.bottom = boardRect.top + boardHeight;\r
2314 \r
2315   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2316   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2317   oldBoardSize = boardSize;\r
2318   oldTinyLayout = tinyLayout;\r
2319   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2320   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2321     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2322   winW *= 1 + twoBoards;\r
2323   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2324   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2325   wpMain.height = winH; //       without disturbing window attachments\r
2326   GetWindowRect(hwndMain, &wrect);\r
2327   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2328                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2329 \r
2330   // [HGM] placement: let attached windows follow size change.\r
2331   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2332   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2333   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2334   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2335   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2336 \r
2337   /* compensate if menu bar wrapped */\r
2338   GetClientRect(hwndMain, &crect);\r
2339   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2340   wpMain.height += offby;\r
2341   switch (flags) {\r
2342   case WMSZ_TOPLEFT:\r
2343     SetWindowPos(hwndMain, NULL, \r
2344                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2345                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2346     break;\r
2347 \r
2348   case WMSZ_TOPRIGHT:\r
2349   case WMSZ_TOP:\r
2350     SetWindowPos(hwndMain, NULL, \r
2351                  wrect.left, wrect.bottom - wpMain.height, \r
2352                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2353     break;\r
2354 \r
2355   case WMSZ_BOTTOMLEFT:\r
2356   case WMSZ_LEFT:\r
2357     SetWindowPos(hwndMain, NULL, \r
2358                  wrect.right - wpMain.width, wrect.top, \r
2359                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2360     break;\r
2361 \r
2362   case WMSZ_BOTTOMRIGHT:\r
2363   case WMSZ_BOTTOM:\r
2364   case WMSZ_RIGHT:\r
2365   default:\r
2366     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2367                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2368     break;\r
2369   }\r
2370 \r
2371   hwndPause = NULL;\r
2372   for (i = 0; i < N_BUTTONS; i++) {\r
2373     if (buttonDesc[i].hwnd != NULL) {\r
2374       DestroyWindow(buttonDesc[i].hwnd);\r
2375       buttonDesc[i].hwnd = NULL;\r
2376     }\r
2377     if (appData.showButtonBar) {\r
2378       buttonDesc[i].hwnd =\r
2379         CreateWindow("BUTTON", buttonDesc[i].label,\r
2380                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2381                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2382                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2383                      (HMENU) buttonDesc[i].id,\r
2384                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2385       if (tinyLayout) {\r
2386         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2387                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2388                     MAKELPARAM(FALSE, 0));\r
2389       }\r
2390       if (buttonDesc[i].id == IDM_Pause)\r
2391         hwndPause = buttonDesc[i].hwnd;\r
2392       buttonDesc[i].wndproc = (WNDPROC)\r
2393         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2394     }\r
2395   }\r
2396   if (gridPen != NULL) DeleteObject(gridPen);\r
2397   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2398   if (premovePen != NULL) DeleteObject(premovePen);\r
2399   if (lineGap != 0) {\r
2400     logbrush.lbStyle = BS_SOLID;\r
2401     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2402     gridPen =\r
2403       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2404                    lineGap, &logbrush, 0, NULL);\r
2405     logbrush.lbColor = highlightSquareColor;\r
2406     highlightPen =\r
2407       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2408                    lineGap, &logbrush, 0, NULL);\r
2409 \r
2410     logbrush.lbColor = premoveHighlightColor; \r
2411     premovePen =\r
2412       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2413                    lineGap, &logbrush, 0, NULL);\r
2414 \r
2415     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2416     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2417       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2418       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2419         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2420       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2421         BOARD_WIDTH * (squareSize + lineGap);\r
2422       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2423     }\r
2424     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2425       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2426       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2427         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2428         lineGap / 2 + (i * (squareSize + lineGap));\r
2429       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2430         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2431       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2432     }\r
2433   }\r
2434 \r
2435   /* [HGM] Licensing requirement */\r
2436 #ifdef GOTHIC\r
2437   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2438 #endif\r
2439 #ifdef FALCON\r
2440   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2441 #endif\r
2442   GothicPopUp( "", VariantNormal);\r
2443 \r
2444 \r
2445 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2446 \r
2447   /* Load piece bitmaps for this board size */\r
2448   for (i=0; i<=2; i++) {\r
2449     for (piece = WhitePawn;\r
2450          (int) piece < (int) BlackPawn;\r
2451          piece = (ChessSquare) ((int) piece + 1)) {\r
2452       if (pieceBitmap[i][piece] != NULL)\r
2453         DeleteObject(pieceBitmap[i][piece]);\r
2454     }\r
2455   }\r
2456 \r
2457   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2458   // Orthodox Chess pieces\r
2459   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2460   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2461   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2462   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2463   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2464   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2465   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2466   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2467   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2468   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2469   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2470   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2471   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2472   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2473   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2474   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2475     // in Shogi, Hijack the unused Queen for Lance\r
2476     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2477     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2478     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2479   } else {\r
2480     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2481     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2482     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2483   }\r
2484 \r
2485   if(squareSize <= 72 && squareSize >= 33) { \r
2486     /* A & C are available in most sizes now */\r
2487     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2488       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2489       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2490       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2491       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2492       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2493       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2494       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2495       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2496       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2497       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2498       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2499       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2500     } else { // Smirf-like\r
2501       if(gameInfo.variant == VariantSChess) {\r
2502         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2503         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2504         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2505       } else {\r
2506         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2507         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2508         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2509       }\r
2510     }\r
2511     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2512       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2513       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2514       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2515     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2516       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2517       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2518       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2519     } else { // WinBoard standard\r
2520       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2521       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2522       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2523     }\r
2524   }\r
2525 \r
2526 \r
2527   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2528     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2529     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2530     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2531     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2532     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2533     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2534     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2535     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2536     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2537     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2538     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2539     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2540     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2541     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2542     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2543     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2544     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2545     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2546     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2547     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2548     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2549     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2550     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2551     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2552     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2553     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2554     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2555     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2556     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2557     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2558 \r
2559     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2560       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2561       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2562       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2563       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2564       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2565       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2566       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2567       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2568       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2569       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2570       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2571       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2572     } else {\r
2573       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2574       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2575       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2576       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2577       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2578       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2579       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2580       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2581       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2582       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2583       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2584       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2585     }\r
2586 \r
2587   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2588     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2589     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2590     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2591     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2592     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2593     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2594     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2595     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2596     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2597     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2598     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2599     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2600     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2601     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2602   }\r
2603 \r
2604 \r
2605   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2606   /* special Shogi support in this size */\r
2607   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2608       for (piece = WhitePawn;\r
2609            (int) piece < (int) BlackPawn;\r
2610            piece = (ChessSquare) ((int) piece + 1)) {\r
2611         if (pieceBitmap[i][piece] != NULL)\r
2612           DeleteObject(pieceBitmap[i][piece]);\r
2613       }\r
2614     }\r
2615   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2616   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2617   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2618   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2619   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2620   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2621   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2622   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2623   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2624   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2625   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2626   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2627   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2628   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2629   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2630   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2631   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2632   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2633   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2634   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2635   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2636   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2637   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2638   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2639   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2640   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2641   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2642   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2643   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2644   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2645   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2646   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2647   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2648   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2649   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2650   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2651   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2652   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2653   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2654   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2655   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2656   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2657   minorSize = 0;\r
2658   }\r
2659 }\r
2660 \r
2661 HBITMAP\r
2662 PieceBitmap(ChessSquare p, int kind)\r
2663 {\r
2664   if ((int) p >= (int) BlackPawn)\r
2665     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2666 \r
2667   return pieceBitmap[kind][(int) p];\r
2668 }\r
2669 \r
2670 /***************************************************************/\r
2671 \r
2672 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2673 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2674 /*\r
2675 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2676 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2677 */\r
2678 \r
2679 VOID\r
2680 SquareToPos(int row, int column, int * x, int * y)\r
2681 {\r
2682   if (flipView) {\r
2683     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2684     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2685   } else {\r
2686     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2687     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2688   }\r
2689 }\r
2690 \r
2691 VOID\r
2692 DrawCoordsOnDC(HDC hdc)\r
2693 {\r
2694   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
2695   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
2696   char str[2] = { NULLCHAR, NULLCHAR };\r
2697   int oldMode, oldAlign, x, y, start, i;\r
2698   HFONT oldFont;\r
2699   HBRUSH oldBrush;\r
2700 \r
2701   if (!appData.showCoords)\r
2702     return;\r
2703 \r
2704   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2705 \r
2706   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2707   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2708   oldAlign = GetTextAlign(hdc);\r
2709   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2710 \r
2711   y = boardRect.top + lineGap;\r
2712   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2713 \r
2714   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2715   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2716     str[0] = files[start + i];\r
2717     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2718     y += squareSize + lineGap;\r
2719   }\r
2720 \r
2721   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2722 \r
2723   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2724   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2725     str[0] = ranks[start + i];\r
2726     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2727     x += squareSize + lineGap;\r
2728   }    \r
2729 \r
2730   SelectObject(hdc, oldBrush);\r
2731   SetBkMode(hdc, oldMode);\r
2732   SetTextAlign(hdc, oldAlign);\r
2733   SelectObject(hdc, oldFont);\r
2734 }\r
2735 \r
2736 VOID\r
2737 DrawGridOnDC(HDC hdc)\r
2738 {\r
2739   HPEN oldPen;\r
2740  \r
2741   if (lineGap != 0) {\r
2742     oldPen = SelectObject(hdc, gridPen);\r
2743     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2744     SelectObject(hdc, oldPen);\r
2745   }\r
2746 }\r
2747 \r
2748 #define HIGHLIGHT_PEN 0\r
2749 #define PREMOVE_PEN   1\r
2750 \r
2751 VOID\r
2752 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2753 {\r
2754   int x1, y1;\r
2755   HPEN oldPen, hPen;\r
2756   if (lineGap == 0) return;\r
2757   if (flipView) {\r
2758     x1 = boardRect.left +\r
2759       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2760     y1 = boardRect.top +\r
2761       lineGap/2 + y * (squareSize + lineGap);\r
2762   } else {\r
2763     x1 = boardRect.left +\r
2764       lineGap/2 + x * (squareSize + lineGap);\r
2765     y1 = boardRect.top +\r
2766       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2767   }\r
2768   hPen = pen ? premovePen : highlightPen;\r
2769   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2770   MoveToEx(hdc, x1, y1, NULL);\r
2771   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2772   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2773   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2774   LineTo(hdc, x1, y1);\r
2775   SelectObject(hdc, oldPen);\r
2776 }\r
2777 \r
2778 VOID\r
2779 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2780 {\r
2781   int i;\r
2782   for (i=0; i<2; i++) {\r
2783     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2784       DrawHighlightOnDC(hdc, TRUE,\r
2785                         h->sq[i].x, h->sq[i].y,\r
2786                         pen);\r
2787   }\r
2788 }\r
2789 \r
2790 /* Note: sqcolor is used only in monoMode */\r
2791 /* Note that this code is largely duplicated in woptions.c,\r
2792    function DrawSampleSquare, so that needs to be updated too */\r
2793 VOID\r
2794 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2795 {\r
2796   HBITMAP oldBitmap;\r
2797   HBRUSH oldBrush;\r
2798   int tmpSize;\r
2799 \r
2800   if (appData.blindfold) return;\r
2801 \r
2802   /* [AS] Use font-based pieces if needed */\r
2803   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2804     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2805     CreatePiecesFromFont();\r
2806 \r
2807     if( fontBitmapSquareSize == squareSize ) {\r
2808         int index = TranslatePieceToFontPiece(piece);\r
2809 \r
2810         SelectObject( tmphdc, hPieceMask[ index ] );\r
2811 \r
2812       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2813         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2814       else\r
2815         BitBlt( hdc,\r
2816             x, y,\r
2817             squareSize, squareSize,\r
2818             tmphdc,\r
2819             0, 0,\r
2820             SRCAND );\r
2821 \r
2822         SelectObject( tmphdc, hPieceFace[ index ] );\r
2823 \r
2824       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2825         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2826       else\r
2827         BitBlt( hdc,\r
2828             x, y,\r
2829             squareSize, squareSize,\r
2830             tmphdc,\r
2831             0, 0,\r
2832             SRCPAINT );\r
2833 \r
2834         return;\r
2835     }\r
2836   }\r
2837 \r
2838   if (appData.monoMode) {\r
2839     SelectObject(tmphdc, PieceBitmap(piece, \r
2840       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2841     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2842            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2843   } else {\r
2844     tmpSize = squareSize;\r
2845     if(minorSize &&\r
2846         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2847          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2848       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2849       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2850       x += (squareSize - minorSize)>>1;\r
2851       y += squareSize - minorSize - 2;\r
2852       tmpSize = minorSize;\r
2853     }\r
2854     if (color || appData.allWhite ) {\r
2855       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2856       if( color )\r
2857               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2858       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2859       if(appData.upsideDown && color==flipView)\r
2860         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2861       else\r
2862         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2863       /* Use black for outline of white pieces */\r
2864       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2865       if(appData.upsideDown && color==flipView)\r
2866         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2867       else\r
2868         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2869     } else {\r
2870       /* Use square color for details of black pieces */\r
2871       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2872       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2873       if(appData.upsideDown && !flipView)\r
2874         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2875       else\r
2876         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2877     }\r
2878     SelectObject(hdc, oldBrush);\r
2879     SelectObject(tmphdc, oldBitmap);\r
2880   }\r
2881 }\r
2882 \r
2883 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2884 int GetBackTextureMode( int algo )\r
2885 {\r
2886     int result = BACK_TEXTURE_MODE_DISABLED;\r
2887 \r
2888     switch( algo ) \r
2889     {\r
2890         case BACK_TEXTURE_MODE_PLAIN:\r
2891             result = 1; /* Always use identity map */\r
2892             break;\r
2893         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2894             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2895             break;\r
2896     }\r
2897 \r
2898     return result;\r
2899 }\r
2900 \r
2901 /* \r
2902     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2903     to handle redraws cleanly (as random numbers would always be different).\r
2904 */\r
2905 VOID RebuildTextureSquareInfo()\r
2906 {\r
2907     BITMAP bi;\r
2908     int lite_w = 0;\r
2909     int lite_h = 0;\r
2910     int dark_w = 0;\r
2911     int dark_h = 0;\r
2912     int row;\r
2913     int col;\r
2914 \r
2915     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2916 \r
2917     if( liteBackTexture != NULL ) {\r
2918         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2919             lite_w = bi.bmWidth;\r
2920             lite_h = bi.bmHeight;\r
2921         }\r
2922     }\r
2923 \r
2924     if( darkBackTexture != NULL ) {\r
2925         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2926             dark_w = bi.bmWidth;\r
2927             dark_h = bi.bmHeight;\r
2928         }\r
2929     }\r
2930 \r
2931     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2932         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2933             if( (col + row) & 1 ) {\r
2934                 /* Lite square */\r
2935                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2936                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2937                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2938                   else\r
2939                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2940                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2941                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2942                   else\r
2943                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2944                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2945                 }\r
2946             }\r
2947             else {\r
2948                 /* Dark square */\r
2949                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2950                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2951                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2952                   else\r
2953                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2954                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2955                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2956                   else\r
2957                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2958                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2959                 }\r
2960             }\r
2961         }\r
2962     }\r
2963 }\r
2964 \r
2965 /* [AS] Arrow highlighting support */\r
2966 \r
2967 static double A_WIDTH = 5; /* Width of arrow body */\r
2968 \r
2969 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2970 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2971 \r
2972 static double Sqr( double x )\r
2973 {\r
2974     return x*x;\r
2975 }\r
2976 \r
2977 static int Round( double x )\r
2978 {\r
2979     return (int) (x + 0.5);\r
2980 }\r
2981 \r
2982 /* Draw an arrow between two points using current settings */\r
2983 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2984 {\r
2985     POINT arrow[7];\r
2986     double dx, dy, j, k, x, y;\r
2987 \r
2988     if( d_x == s_x ) {\r
2989         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2990 \r
2991         arrow[0].x = s_x + A_WIDTH + 0.5;\r
2992         arrow[0].y = s_y;\r
2993 \r
2994         arrow[1].x = s_x + A_WIDTH + 0.5;\r
2995         arrow[1].y = d_y - h;\r
2996 \r
2997         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2998         arrow[2].y = d_y - h;\r
2999 \r
3000         arrow[3].x = d_x;\r
3001         arrow[3].y = d_y;\r
3002 \r
3003         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3004         arrow[5].y = d_y - h;\r
3005 \r
3006         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3007         arrow[4].y = d_y - h;\r
3008 \r
3009         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3010         arrow[6].y = s_y;\r
3011     }\r
3012     else if( d_y == s_y ) {\r
3013         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3014 \r
3015         arrow[0].x = s_x;\r
3016         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3017 \r
3018         arrow[1].x = d_x - w;\r
3019         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3020 \r
3021         arrow[2].x = d_x - w;\r
3022         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3023 \r
3024         arrow[3].x = d_x;\r
3025         arrow[3].y = d_y;\r
3026 \r
3027         arrow[5].x = d_x - w;\r
3028         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3029 \r
3030         arrow[4].x = d_x - w;\r
3031         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3032 \r
3033         arrow[6].x = s_x;\r
3034         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3035     }\r
3036     else {\r
3037         /* [AS] Needed a lot of paper for this! :-) */\r
3038         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3039         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3040   \r
3041         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3042 \r
3043         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3044 \r
3045         x = s_x;\r
3046         y = s_y;\r
3047 \r
3048         arrow[0].x = Round(x - j);\r
3049         arrow[0].y = Round(y + j*dx);\r
3050 \r
3051         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3052         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3053 \r
3054         if( d_x > s_x ) {\r
3055             x = (double) d_x - k;\r
3056             y = (double) d_y - k*dy;\r
3057         }\r
3058         else {\r
3059             x = (double) d_x + k;\r
3060             y = (double) d_y + k*dy;\r
3061         }\r
3062 \r
3063         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3064 \r
3065         arrow[6].x = Round(x - j);\r
3066         arrow[6].y = Round(y + j*dx);\r
3067 \r
3068         arrow[2].x = Round(arrow[6].x + 2*j);\r
3069         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3070 \r
3071         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3072         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3073 \r
3074         arrow[4].x = d_x;\r
3075         arrow[4].y = d_y;\r
3076 \r
3077         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3078         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3079     }\r
3080 \r
3081     Polygon( hdc, arrow, 7 );\r
3082 }\r
3083 \r
3084 /* [AS] Draw an arrow between two squares */\r
3085 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3086 {\r
3087     int s_x, s_y, d_x, d_y;\r
3088     HPEN hpen;\r
3089     HPEN holdpen;\r
3090     HBRUSH hbrush;\r
3091     HBRUSH holdbrush;\r
3092     LOGBRUSH stLB;\r
3093 \r
3094     if( s_col == d_col && s_row == d_row ) {\r
3095         return;\r
3096     }\r
3097 \r
3098     /* Get source and destination points */\r
3099     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3100     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3101 \r
3102     if( d_y > s_y ) {\r
3103         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3104     }\r
3105     else if( d_y < s_y ) {\r
3106         d_y += squareSize / 2 + squareSize / 4;\r
3107     }\r
3108     else {\r
3109         d_y += squareSize / 2;\r
3110     }\r
3111 \r
3112     if( d_x > s_x ) {\r
3113         d_x += squareSize / 2 - squareSize / 4;\r
3114     }\r
3115     else if( d_x < s_x ) {\r
3116         d_x += squareSize / 2 + squareSize / 4;\r
3117     }\r
3118     else {\r
3119         d_x += squareSize / 2;\r
3120     }\r
3121 \r
3122     s_x += squareSize / 2;\r
3123     s_y += squareSize / 2;\r
3124 \r
3125     /* Adjust width */\r
3126     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3127 \r
3128     /* Draw */\r
3129     stLB.lbStyle = BS_SOLID;\r
3130     stLB.lbColor = appData.highlightArrowColor;\r
3131     stLB.lbHatch = 0;\r
3132 \r
3133     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3134     holdpen = SelectObject( hdc, hpen );\r
3135     hbrush = CreateBrushIndirect( &stLB );\r
3136     holdbrush = SelectObject( hdc, hbrush );\r
3137 \r
3138     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3139 \r
3140     SelectObject( hdc, holdpen );\r
3141     SelectObject( hdc, holdbrush );\r
3142     DeleteObject( hpen );\r
3143     DeleteObject( hbrush );\r
3144 }\r
3145 \r
3146 BOOL HasHighlightInfo()\r
3147 {\r
3148     BOOL result = FALSE;\r
3149 \r
3150     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3151         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3152     {\r
3153         result = TRUE;\r
3154     }\r
3155 \r
3156     return result;\r
3157 }\r
3158 \r
3159 BOOL IsDrawArrowEnabled()\r
3160 {\r
3161     BOOL result = FALSE;\r
3162 \r
3163     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3164         result = TRUE;\r
3165     }\r
3166 \r
3167     return result;\r
3168 }\r
3169 \r
3170 VOID DrawArrowHighlight( HDC hdc )\r
3171 {\r
3172     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3173         DrawArrowBetweenSquares( hdc,\r
3174             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3175             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3176     }\r
3177 }\r
3178 \r
3179 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3180 {\r
3181     HRGN result = NULL;\r
3182 \r
3183     if( HasHighlightInfo() ) {\r
3184         int x1, y1, x2, y2;\r
3185         int sx, sy, dx, dy;\r
3186 \r
3187         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3188         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3189 \r
3190         sx = MIN( x1, x2 );\r
3191         sy = MIN( y1, y2 );\r
3192         dx = MAX( x1, x2 ) + squareSize;\r
3193         dy = MAX( y1, y2 ) + squareSize;\r
3194 \r
3195         result = CreateRectRgn( sx, sy, dx, dy );\r
3196     }\r
3197 \r
3198     return result;\r
3199 }\r
3200 \r
3201 /*\r
3202     Warning: this function modifies the behavior of several other functions. \r
3203     \r
3204     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3205     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3206     repaint is scattered all over the place, which is not good for features such as\r
3207     "arrow highlighting" that require a full repaint of the board.\r
3208 \r
3209     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3210     user interaction, when speed is not so important) but especially to avoid errors\r
3211     in the displayed graphics.\r
3212 \r
3213     In such patched places, I always try refer to this function so there is a single\r
3214     place to maintain knowledge.\r
3215     \r
3216     To restore the original behavior, just return FALSE unconditionally.\r
3217 */\r
3218 BOOL IsFullRepaintPreferrable()\r
3219 {\r
3220     BOOL result = FALSE;\r
3221 \r
3222     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3223         /* Arrow may appear on the board */\r
3224         result = TRUE;\r
3225     }\r
3226 \r
3227     return result;\r
3228 }\r
3229 \r
3230 /* \r
3231     This function is called by DrawPosition to know whether a full repaint must\r
3232     be forced or not.\r
3233 \r
3234     Only DrawPosition may directly call this function, which makes use of \r
3235     some state information. Other function should call DrawPosition specifying \r
3236     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3237 */\r
3238 BOOL DrawPositionNeedsFullRepaint()\r
3239 {\r
3240     BOOL result = FALSE;\r
3241 \r
3242     /* \r
3243         Probably a slightly better policy would be to trigger a full repaint\r
3244         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3245         but animation is fast enough that it's difficult to notice.\r
3246     */\r
3247     if( animInfo.piece == EmptySquare ) {\r
3248         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3249             result = TRUE;\r
3250         }\r
3251     }\r
3252 \r
3253     return result;\r
3254 }\r
3255 \r
3256 VOID\r
3257 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3258 {\r
3259   int row, column, x, y, square_color, piece_color;\r
3260   ChessSquare piece;\r
3261   HBRUSH oldBrush;\r
3262   HDC texture_hdc = NULL;\r
3263 \r
3264   /* [AS] Initialize background textures if needed */\r
3265   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3266       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3267       if( backTextureSquareSize != squareSize \r
3268        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3269           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3270           backTextureSquareSize = squareSize;\r
3271           RebuildTextureSquareInfo();\r
3272       }\r
3273 \r
3274       texture_hdc = CreateCompatibleDC( hdc );\r
3275   }\r
3276 \r
3277   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3278     for (column = 0; column < BOARD_WIDTH; column++) {\r
3279   \r
3280       SquareToPos(row, column, &x, &y);\r
3281 \r
3282       piece = board[row][column];\r
3283 \r
3284       square_color = ((column + row) % 2) == 1;\r
3285       if( gameInfo.variant == VariantXiangqi ) {\r
3286           square_color = !InPalace(row, column);\r
3287           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3288           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3289       }\r
3290       piece_color = (int) piece < (int) BlackPawn;\r
3291 \r
3292 \r
3293       /* [HGM] holdings file: light square or black */\r
3294       if(column == BOARD_LEFT-2) {\r
3295             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3296                 square_color = 1;\r
3297             else {\r
3298                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3299                 continue;\r
3300             }\r
3301       } else\r
3302       if(column == BOARD_RGHT + 1 ) {\r
3303             if( row < gameInfo.holdingsSize )\r
3304                 square_color = 1;\r
3305             else {\r
3306                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3307                 continue;\r
3308             }\r
3309       }\r
3310       if(column == BOARD_LEFT-1 ) /* left align */\r
3311             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3312       else if( column == BOARD_RGHT) /* right align */\r
3313             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3314       else\r
3315       if (appData.monoMode) {\r
3316         if (piece == EmptySquare) {\r
3317           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3318                  square_color ? WHITENESS : BLACKNESS);\r
3319         } else {\r
3320           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3321         }\r
3322       } \r
3323       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3324           /* [AS] Draw the square using a texture bitmap */\r
3325           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3326           int r = row, c = column; // [HGM] do not flip board in flipView\r
3327           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3328 \r
3329           DrawTile( x, y, \r
3330               squareSize, squareSize, \r
3331               hdc, \r
3332               texture_hdc,\r
3333               backTextureSquareInfo[r][c].mode,\r
3334               backTextureSquareInfo[r][c].x,\r
3335               backTextureSquareInfo[r][c].y );\r
3336 \r
3337           SelectObject( texture_hdc, hbm );\r
3338 \r
3339           if (piece != EmptySquare) {\r
3340               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3341           }\r
3342       }\r
3343       else {\r
3344         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3345 \r
3346         oldBrush = SelectObject(hdc, brush );\r
3347         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3348         SelectObject(hdc, oldBrush);\r
3349         if (piece != EmptySquare)\r
3350           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3351       }\r
3352     }\r
3353   }\r
3354 \r
3355   if( texture_hdc != NULL ) {\r
3356     DeleteDC( texture_hdc );\r
3357   }\r
3358 }\r
3359 \r
3360 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3361 void fputDW(FILE *f, int x)\r
3362 {\r
3363         fputc(x     & 255, f);\r
3364         fputc(x>>8  & 255, f);\r
3365         fputc(x>>16 & 255, f);\r
3366         fputc(x>>24 & 255, f);\r
3367 }\r
3368 \r
3369 #define MAX_CLIPS 200   /* more than enough */\r
3370 \r
3371 VOID\r
3372 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3373 {\r
3374 //  HBITMAP bufferBitmap;\r
3375   BITMAP bi;\r
3376 //  RECT Rect;\r
3377   HDC tmphdc;\r
3378   HBITMAP hbm;\r
3379   int w = 100, h = 50;\r
3380 \r
3381   if(logo == NULL) return;\r
3382 //  GetClientRect(hwndMain, &Rect);\r
3383 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3384 //                                      Rect.bottom-Rect.top+1);\r
3385   tmphdc = CreateCompatibleDC(hdc);\r
3386   hbm = SelectObject(tmphdc, logo);\r
3387   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3388             w = bi.bmWidth;\r
3389             h = bi.bmHeight;\r
3390   }\r
3391   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3392                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3393   SelectObject(tmphdc, hbm);\r
3394   DeleteDC(tmphdc);\r
3395 }\r
3396 \r
3397 VOID\r
3398 DisplayLogos()\r
3399 {\r
3400   if(logoHeight) {\r
3401         HDC hdc = GetDC(hwndMain);\r
3402         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3403         if(appData.autoLogo) {\r
3404           \r
3405           switch(gameMode) { // pick logos based on game mode\r
3406             case IcsObserving:\r
3407                 whiteLogo = second.programLogo; // ICS logo\r
3408                 blackLogo = second.programLogo;\r
3409             default:\r
3410                 break;\r
3411             case IcsPlayingWhite:\r
3412                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3413                 blackLogo = second.programLogo; // ICS logo\r
3414                 break;\r
3415             case IcsPlayingBlack:\r
3416                 whiteLogo = second.programLogo; // ICS logo\r
3417                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3418                 break;\r
3419             case TwoMachinesPlay:\r
3420                 if(first.twoMachinesColor[0] == 'b') {\r
3421                     whiteLogo = second.programLogo;\r
3422                     blackLogo = first.programLogo;\r
3423                 }\r
3424                 break;\r
3425             case MachinePlaysWhite:\r
3426                 blackLogo = userLogo;\r
3427                 break;\r
3428             case MachinePlaysBlack:\r
3429                 whiteLogo = userLogo;\r
3430                 blackLogo = first.programLogo;\r
3431           }\r
3432         }\r
3433         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3434         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3435         ReleaseDC(hwndMain, hdc);\r
3436   }\r
3437 }\r
3438 \r
3439 static HDC hdcSeek;\r
3440 \r
3441 // [HGM] seekgraph\r
3442 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3443 {\r
3444     POINT stPt;\r
3445     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3446     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3447     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3448     SelectObject( hdcSeek, hp );\r
3449 }\r
3450 \r
3451 // front-end wrapper for drawing functions to do rectangles\r
3452 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3453 {\r
3454     HPEN hp;\r
3455     RECT rc;\r
3456 \r
3457     if (hdcSeek == NULL) {\r
3458     hdcSeek = GetDC(hwndMain);\r
3459       if (!appData.monoMode) {\r
3460         SelectPalette(hdcSeek, hPal, FALSE);\r
3461         RealizePalette(hdcSeek);\r
3462       }\r
3463     }\r
3464     hp = SelectObject( hdcSeek, gridPen );\r
3465     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3466     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3467     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3468     SelectObject( hdcSeek, hp );\r
3469 }\r
3470 \r
3471 // front-end wrapper for putting text in graph\r
3472 void DrawSeekText(char *buf, int x, int y)\r
3473 {\r
3474         SIZE stSize;\r
3475         SetBkMode( hdcSeek, TRANSPARENT );\r
3476         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3477         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3478 }\r
3479 \r
3480 void DrawSeekDot(int x, int y, int color)\r
3481 {\r
3482         int square = color & 0x80;\r
3483         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3484                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3485         color &= 0x7F;\r
3486         if(square)\r
3487             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3488                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3489         else\r
3490             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3491                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3492             SelectObject(hdcSeek, oldBrush);\r
3493 }\r
3494 \r
3495 VOID\r
3496 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3497 {\r
3498   static Board lastReq[2], lastDrawn[2];\r
3499   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3500   static int lastDrawnFlipView = 0;\r
3501   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3502   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3503   HDC tmphdc;\r
3504   HDC hdcmem;\r
3505   HBITMAP bufferBitmap;\r
3506   HBITMAP oldBitmap;\r
3507   RECT Rect;\r
3508   HRGN clips[MAX_CLIPS];\r
3509   ChessSquare dragged_piece = EmptySquare;\r
3510   int nr = twoBoards*partnerUp;\r
3511 \r
3512   /* I'm undecided on this - this function figures out whether a full\r
3513    * repaint is necessary on its own, so there's no real reason to have the\r
3514    * caller tell it that.  I think this can safely be set to FALSE - but\r
3515    * if we trust the callers not to request full repaints unnessesarily, then\r
3516    * we could skip some clipping work.  In other words, only request a full\r
3517    * redraw when the majority of pieces have changed positions (ie. flip, \r
3518    * gamestart and similar)  --Hawk\r
3519    */\r
3520   Boolean fullrepaint = repaint;\r
3521 \r
3522   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3523 \r
3524   if( DrawPositionNeedsFullRepaint() ) {\r
3525       fullrepaint = TRUE;\r
3526   }\r
3527 \r
3528   if (board == NULL) {\r
3529     if (!lastReqValid[nr]) {\r
3530       return;\r
3531     }\r
3532     board = lastReq[nr];\r
3533   } else {\r
3534     CopyBoard(lastReq[nr], board);\r
3535     lastReqValid[nr] = 1;\r
3536   }\r
3537 \r
3538   if (doingSizing) {\r
3539     return;\r
3540   }\r
3541 \r
3542   if (IsIconic(hwndMain)) {\r
3543     return;\r
3544   }\r
3545 \r
3546   if (hdc == NULL) {\r
3547     hdc = GetDC(hwndMain);\r
3548     if (!appData.monoMode) {\r
3549       SelectPalette(hdc, hPal, FALSE);\r
3550       RealizePalette(hdc);\r
3551     }\r
3552     releaseDC = TRUE;\r
3553   } else {\r
3554     releaseDC = FALSE;\r
3555   }\r
3556 \r
3557   /* Create some work-DCs */\r
3558   hdcmem = CreateCompatibleDC(hdc);\r
3559   tmphdc = CreateCompatibleDC(hdc);\r
3560 \r
3561   /* If dragging is in progress, we temporarely remove the piece */\r
3562   /* [HGM] or temporarily decrease count if stacked              */\r
3563   /*       !! Moved to before board compare !!                   */\r
3564   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3565     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3566     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3567             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3568         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3569     } else \r
3570     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3571             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3572         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3573     } else \r
3574         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3575   }\r
3576 \r
3577   /* Figure out which squares need updating by comparing the \r
3578    * newest board with the last drawn board and checking if\r
3579    * flipping has changed.\r
3580    */\r
3581   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3582     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3583       for (column = 0; column < BOARD_WIDTH; column++) {\r
3584         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3585           SquareToPos(row, column, &x, &y);\r
3586           clips[num_clips++] =\r
3587             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3588         }\r
3589       }\r
3590     }\r
3591    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3592     for (i=0; i<2; i++) {\r
3593       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3594           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3595         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3596             lastDrawnHighlight.sq[i].y >= 0) {\r
3597           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3598                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3599           clips[num_clips++] =\r
3600             CreateRectRgn(x - lineGap, y - lineGap, \r
3601                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3602         }\r
3603         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3604           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3605           clips[num_clips++] =\r
3606             CreateRectRgn(x - lineGap, y - lineGap, \r
3607                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3608         }\r
3609       }\r
3610     }\r
3611     for (i=0; i<2; i++) {\r
3612       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3613           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3614         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3615             lastDrawnPremove.sq[i].y >= 0) {\r
3616           SquareToPos(lastDrawnPremove.sq[i].y,\r
3617                       lastDrawnPremove.sq[i].x, &x, &y);\r
3618           clips[num_clips++] =\r
3619             CreateRectRgn(x - lineGap, y - lineGap, \r
3620                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3621         }\r
3622         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3623             premoveHighlightInfo.sq[i].y >= 0) {\r
3624           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3625                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3626           clips[num_clips++] =\r
3627             CreateRectRgn(x - lineGap, y - lineGap, \r
3628                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3629         }\r
3630       }\r
3631     }\r
3632    } else { // nr == 1\r
3633         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3634         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3635         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3636         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3637       for (i=0; i<2; i++) {\r
3638         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3639             partnerHighlightInfo.sq[i].y >= 0) {\r
3640           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3641                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3642           clips[num_clips++] =\r
3643             CreateRectRgn(x - lineGap, y - lineGap, \r
3644                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3645         }\r
3646         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3647             oldPartnerHighlight.sq[i].y >= 0) {\r
3648           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3649                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3650           clips[num_clips++] =\r
3651             CreateRectRgn(x - lineGap, y - lineGap, \r
3652                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3653         }\r
3654       }\r
3655    }\r
3656   } else {\r
3657     fullrepaint = TRUE;\r
3658   }\r
3659 \r
3660   /* Create a buffer bitmap - this is the actual bitmap\r
3661    * being written to.  When all the work is done, we can\r
3662    * copy it to the real DC (the screen).  This avoids\r
3663    * the problems with flickering.\r
3664    */\r
3665   GetClientRect(hwndMain, &Rect);\r
3666   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3667                                         Rect.bottom-Rect.top+1);\r
3668   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3669   if (!appData.monoMode) {\r
3670     SelectPalette(hdcmem, hPal, FALSE);\r
3671   }\r
3672 \r
3673   /* Create clips for dragging */\r
3674   if (!fullrepaint) {\r
3675     if (dragInfo.from.x >= 0) {\r
3676       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3677       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3678     }\r
3679     if (dragInfo.start.x >= 0) {\r
3680       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3681       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3682     }\r
3683     if (dragInfo.pos.x >= 0) {\r
3684       x = dragInfo.pos.x - squareSize / 2;\r
3685       y = dragInfo.pos.y - squareSize / 2;\r
3686       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3687     }\r
3688     if (dragInfo.lastpos.x >= 0) {\r
3689       x = dragInfo.lastpos.x - squareSize / 2;\r
3690       y = dragInfo.lastpos.y - squareSize / 2;\r
3691       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3692     }\r
3693   }\r
3694 \r
3695   /* Are we animating a move?  \r
3696    * If so, \r
3697    *   - remove the piece from the board (temporarely)\r
3698    *   - calculate the clipping region\r
3699    */\r
3700   if (!fullrepaint) {\r
3701     if (animInfo.piece != EmptySquare) {\r
3702       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3703       x = boardRect.left + animInfo.lastpos.x;\r
3704       y = boardRect.top + animInfo.lastpos.y;\r
3705       x2 = boardRect.left + animInfo.pos.x;\r
3706       y2 = boardRect.top + animInfo.pos.y;\r
3707       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3708       /* Slight kludge.  The real problem is that after AnimateMove is\r
3709          done, the position on the screen does not match lastDrawn.\r
3710          This currently causes trouble only on e.p. captures in\r
3711          atomic, where the piece moves to an empty square and then\r
3712          explodes.  The old and new positions both had an empty square\r
3713          at the destination, but animation has drawn a piece there and\r
3714          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3715       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3716     }\r
3717   }\r
3718 \r
3719   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3720   if (num_clips == 0)\r
3721     fullrepaint = TRUE;\r
3722 \r
3723   /* Set clipping on the memory DC */\r
3724   if (!fullrepaint) {\r
3725     SelectClipRgn(hdcmem, clips[0]);\r
3726     for (x = 1; x < num_clips; x++) {\r
3727       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3728         abort();  // this should never ever happen!\r
3729     }\r
3730   }\r
3731 \r
3732   /* Do all the drawing to the memory DC */\r
3733   if(explodeInfo.radius) { // [HGM] atomic\r
3734         HBRUSH oldBrush;\r
3735         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3736         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3737         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3738         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3739         x += squareSize/2;\r
3740         y += squareSize/2;\r
3741         if(!fullrepaint) {\r
3742           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3743           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3744         }\r
3745         DrawGridOnDC(hdcmem);\r
3746         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3747         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3748         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3749         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3750         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3751         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3752         SelectObject(hdcmem, oldBrush);\r
3753   } else {\r
3754     DrawGridOnDC(hdcmem);\r
3755     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3756         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3757         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3758     } else {\r
3759         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3760         oldPartnerHighlight = partnerHighlightInfo;\r
3761     }\r
3762     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3763   }\r
3764   if(nr == 0) // [HGM] dual: markers only on left board\r
3765   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3766     for (column = 0; column < BOARD_WIDTH; column++) {\r
3767         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3768             HBRUSH oldBrush = SelectObject(hdcmem, \r
3769                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3770             SquareToPos(row, column, &x, &y);\r
3771             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3772                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3773             SelectObject(hdcmem, oldBrush);\r
3774         }\r
3775     }\r
3776   }\r
3777 \r
3778   if( appData.highlightMoveWithArrow ) {\r
3779     DrawArrowHighlight(hdcmem);\r
3780   }\r
3781 \r
3782   DrawCoordsOnDC(hdcmem);\r
3783 \r
3784   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3785                  /* to make sure lastDrawn contains what is actually drawn */\r
3786 \r
3787   /* Put the dragged piece back into place and draw it (out of place!) */\r
3788     if (dragged_piece != EmptySquare) {\r
3789     /* [HGM] or restack */\r
3790     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3791                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3792     else\r
3793     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3794                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3795     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3796     x = dragInfo.pos.x - squareSize / 2;\r
3797     y = dragInfo.pos.y - squareSize / 2;\r
3798     DrawPieceOnDC(hdcmem, dragged_piece,\r
3799                   ((int) dragged_piece < (int) BlackPawn), \r
3800                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3801   }   \r
3802   \r
3803   /* Put the animated piece back into place and draw it */\r
3804   if (animInfo.piece != EmptySquare) {\r
3805     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3806     x = boardRect.left + animInfo.pos.x;\r
3807     y = boardRect.top + animInfo.pos.y;\r
3808     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3809                   ((int) animInfo.piece < (int) BlackPawn),\r
3810                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3811   }\r
3812 \r
3813   /* Release the bufferBitmap by selecting in the old bitmap \r
3814    * and delete the memory DC\r
3815    */\r
3816   SelectObject(hdcmem, oldBitmap);\r
3817   DeleteDC(hdcmem);\r
3818 \r
3819   /* Set clipping on the target DC */\r
3820   if (!fullrepaint) {\r
3821     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3822         RECT rect;\r
3823         GetRgnBox(clips[x], &rect);\r
3824         DeleteObject(clips[x]);\r
3825         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3826                           rect.right + wpMain.width/2, rect.bottom);\r
3827     }\r
3828     SelectClipRgn(hdc, clips[0]);\r
3829     for (x = 1; x < num_clips; x++) {\r
3830       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3831         abort();   // this should never ever happen!\r
3832     } \r
3833   }\r
3834 \r
3835   /* Copy the new bitmap onto the screen in one go.\r
3836    * This way we avoid any flickering\r
3837    */\r
3838   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3839   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3840          boardRect.right - boardRect.left,\r
3841          boardRect.bottom - boardRect.top,\r
3842          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3843   if(saveDiagFlag) { \r
3844     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3845     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3846 \r
3847     GetObject(bufferBitmap, sizeof(b), &b);\r
3848     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3849         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3850         bih.biWidth = b.bmWidth;\r
3851         bih.biHeight = b.bmHeight;\r
3852         bih.biPlanes = 1;\r
3853         bih.biBitCount = b.bmBitsPixel;\r
3854         bih.biCompression = 0;\r
3855         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3856         bih.biXPelsPerMeter = 0;\r
3857         bih.biYPelsPerMeter = 0;\r
3858         bih.biClrUsed = 0;\r
3859         bih.biClrImportant = 0;\r
3860 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3861 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3862         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3863 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3864 \r
3865         wb = b.bmWidthBytes;\r
3866         // count colors\r
3867         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3868                 int k = ((int*) pData)[i];\r
3869                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3870                 if(j >= 16) break;\r
3871                 color[j] = k;\r
3872                 if(j >= nrColors) nrColors = j+1;\r
3873         }\r
3874         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3875                 INT p = 0;\r
3876                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3877                     for(w=0; w<(wb>>2); w+=2) {\r
3878                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3879                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3880                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3881                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3882                         pData[p++] = m | j<<4;\r
3883                     }\r
3884                     while(p&3) pData[p++] = 0;\r
3885                 }\r
3886                 fac = 3;\r
3887                 wb = ((wb+31)>>5)<<2;\r
3888         }\r
3889         // write BITMAPFILEHEADER\r
3890         fprintf(diagFile, "BM");\r
3891         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3892         fputDW(diagFile, 0);\r
3893         fputDW(diagFile, 0x36 + (fac?64:0));\r
3894         // write BITMAPINFOHEADER\r
3895         fputDW(diagFile, 40);\r
3896         fputDW(diagFile, b.bmWidth);\r
3897         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3898         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3899         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3900         fputDW(diagFile, 0);\r
3901         fputDW(diagFile, 0);\r
3902         fputDW(diagFile, 0);\r
3903         fputDW(diagFile, 0);\r
3904         fputDW(diagFile, 0);\r
3905         fputDW(diagFile, 0);\r
3906         // write color table\r
3907         if(fac)\r
3908         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3909         // write bitmap data\r
3910         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3911                 fputc(pData[i], diagFile);\r
3912      }\r
3913   }\r
3914 \r
3915   SelectObject(tmphdc, oldBitmap);\r
3916 \r
3917   /* Massive cleanup */\r
3918   for (x = 0; x < num_clips; x++)\r
3919     DeleteObject(clips[x]);\r
3920 \r
3921   DeleteDC(tmphdc);\r
3922   DeleteObject(bufferBitmap);\r
3923 \r
3924   if (releaseDC) \r
3925     ReleaseDC(hwndMain, hdc);\r
3926   \r
3927   if (lastDrawnFlipView != flipView && nr == 0) {\r
3928     if (flipView)\r
3929       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3930     else\r
3931       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3932   }\r
3933 \r
3934 /*  CopyBoard(lastDrawn, board);*/\r
3935   lastDrawnHighlight = highlightInfo;\r
3936   lastDrawnPremove   = premoveHighlightInfo;\r
3937   lastDrawnFlipView = flipView;\r
3938   lastDrawnValid[nr] = 1;\r
3939 }\r
3940 \r
3941 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3942 int\r
3943 SaveDiagram(f)\r
3944      FILE *f;\r
3945 {\r
3946     saveDiagFlag = 1; diagFile = f;\r
3947     HDCDrawPosition(NULL, TRUE, NULL);\r
3948 \r
3949     saveDiagFlag = 0;\r
3950 \r
3951 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3952     \r
3953     fclose(f);\r
3954     return TRUE;\r
3955 }\r
3956 \r
3957 \r
3958 /*---------------------------------------------------------------------------*\\r
3959 | CLIENT PAINT PROCEDURE\r
3960 |   This is the main event-handler for the WM_PAINT message.\r
3961 |\r
3962 \*---------------------------------------------------------------------------*/\r
3963 VOID\r
3964 PaintProc(HWND hwnd)\r
3965 {\r
3966   HDC         hdc;\r
3967   PAINTSTRUCT ps;\r
3968   HFONT       oldFont;\r
3969 \r
3970   if((hdc = BeginPaint(hwnd, &ps))) {\r
3971     if (IsIconic(hwnd)) {\r
3972       DrawIcon(hdc, 2, 2, iconCurrent);\r
3973     } else {\r
3974       if (!appData.monoMode) {\r
3975         SelectPalette(hdc, hPal, FALSE);\r
3976         RealizePalette(hdc);\r
3977       }\r
3978       HDCDrawPosition(hdc, 1, NULL);\r
3979       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3980         flipView = !flipView; partnerUp = !partnerUp;\r
3981         HDCDrawPosition(hdc, 1, NULL);\r
3982         flipView = !flipView; partnerUp = !partnerUp;\r
3983       }\r
3984       oldFont =\r
3985         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3986       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3987                  ETO_CLIPPED|ETO_OPAQUE,\r
3988                  &messageRect, messageText, strlen(messageText), NULL);\r
3989       SelectObject(hdc, oldFont);\r
3990       DisplayBothClocks();\r
3991       DisplayLogos();\r
3992     }\r
3993     EndPaint(hwnd,&ps);\r
3994   }\r
3995 \r
3996   return;\r
3997 }\r
3998 \r
3999 \r
4000 /*\r
4001  * If the user selects on a border boundary, return -1; if off the board,\r
4002  *   return -2.  Otherwise map the event coordinate to the square.\r
4003  * The offset boardRect.left or boardRect.top must already have been\r
4004  *   subtracted from x.\r
4005  */\r
4006 int EventToSquare(x, limit)\r
4007      int x, limit;\r
4008 {\r
4009   if (x <= 0)\r
4010     return -2;\r
4011   if (x < lineGap)\r
4012     return -1;\r
4013   x -= lineGap;\r
4014   if ((x % (squareSize + lineGap)) >= squareSize)\r
4015     return -1;\r
4016   x /= (squareSize + lineGap);\r
4017     if (x >= limit)\r
4018     return -2;\r
4019   return x;\r
4020 }\r
4021 \r
4022 typedef struct {\r
4023   char piece;\r
4024   int command;\r
4025   char* name;\r
4026 } DropEnable;\r
4027 \r
4028 DropEnable dropEnables[] = {\r
4029   { 'P', DP_Pawn, N_("Pawn") },\r
4030   { 'N', DP_Knight, N_("Knight") },\r
4031   { 'B', DP_Bishop, N_("Bishop") },\r
4032   { 'R', DP_Rook, N_("Rook") },\r
4033   { 'Q', DP_Queen, N_("Queen") },\r
4034 };\r
4035 \r
4036 VOID\r
4037 SetupDropMenu(HMENU hmenu)\r
4038 {\r
4039   int i, count, enable;\r
4040   char *p;\r
4041   extern char white_holding[], black_holding[];\r
4042   char item[MSG_SIZ];\r
4043 \r
4044   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4045     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4046                dropEnables[i].piece);\r
4047     count = 0;\r
4048     while (p && *p++ == dropEnables[i].piece) count++;\r
4049       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4050     enable = count > 0 || !appData.testLegality\r
4051       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4052                       && !appData.icsActive);\r
4053     ModifyMenu(hmenu, dropEnables[i].command,\r
4054                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4055                dropEnables[i].command, item);\r
4056   }\r
4057 }\r
4058 \r
4059 void DragPieceBegin(int x, int y)\r
4060 {\r
4061       dragInfo.lastpos.x = boardRect.left + x;\r
4062       dragInfo.lastpos.y = boardRect.top + y;\r
4063       dragInfo.from.x = fromX;\r
4064       dragInfo.from.y = fromY;\r
4065       dragInfo.start = dragInfo.from;\r
4066       SetCapture(hwndMain);\r
4067 }\r
4068 \r
4069 void DragPieceEnd(int x, int y)\r
4070 {\r
4071     ReleaseCapture();\r
4072     dragInfo.start.x = dragInfo.start.y = -1;\r
4073     dragInfo.from = dragInfo.start;\r
4074     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4075 }\r
4076 \r
4077 /* Event handler for mouse messages */\r
4078 VOID\r
4079 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4080 {\r
4081   int x, y, menuNr;\r
4082   POINT pt;\r
4083   static int recursive = 0;\r
4084   HMENU hmenu;\r
4085   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4086 \r
4087   if (recursive) {\r
4088     if (message == WM_MBUTTONUP) {\r
4089       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4090          to the middle button: we simulate pressing the left button too!\r
4091          */\r
4092       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4093       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4094     }\r
4095     return;\r
4096   }\r
4097   recursive++;\r
4098   \r
4099   pt.x = LOWORD(lParam);\r
4100   pt.y = HIWORD(lParam);\r
4101   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4102   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4103   if (!flipView && y >= 0) {\r
4104     y = BOARD_HEIGHT - 1 - y;\r
4105   }\r
4106   if (flipView && x >= 0) {\r
4107     x = BOARD_WIDTH - 1 - x;\r
4108   }\r
4109 \r
4110   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4111 \r
4112   switch (message) {\r
4113   case WM_LBUTTONDOWN:\r
4114       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4115         ClockClick(flipClock);\r
4116       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4117         ClockClick(!flipClock);\r
4118       }\r
4119       dragInfo.start.x = dragInfo.start.y = -1;\r
4120       dragInfo.from = dragInfo.start;\r
4121     if(fromX == -1 && frozen) { // not sure where this is for\r
4122                 fromX = fromY = -1; \r
4123       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4124       break;\r
4125     }\r
4126       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4127       DrawPosition(TRUE, NULL);\r
4128     break;\r
4129 \r
4130   case WM_LBUTTONUP:\r
4131       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4132       DrawPosition(TRUE, NULL);\r
4133     break;\r
4134 \r
4135   case WM_MOUSEMOVE:\r
4136     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4137     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4138     if ((appData.animateDragging || appData.highlightDragging)\r
4139         && (wParam & MK_LBUTTON)\r
4140         && dragInfo.from.x >= 0) \r
4141     {\r
4142       BOOL full_repaint = FALSE;\r
4143 \r
4144       if (appData.animateDragging) {\r
4145         dragInfo.pos = pt;\r
4146       }\r
4147       if (appData.highlightDragging) {\r
4148         SetHighlights(fromX, fromY, x, y);\r
4149         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4150             full_repaint = TRUE;\r
4151         }\r
4152       }\r
4153       \r
4154       DrawPosition( full_repaint, NULL);\r
4155       \r
4156       dragInfo.lastpos = dragInfo.pos;\r
4157     }\r
4158     break;\r
4159 \r
4160   case WM_MOUSEWHEEL: // [DM]\r
4161     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4162        /* Mouse Wheel is being rolled forward\r
4163         * Play moves forward\r
4164         */\r
4165        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4166                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4167        /* Mouse Wheel is being rolled backward\r
4168         * Play moves backward\r
4169         */\r
4170        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4171                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4172     }\r
4173     break;\r
4174 \r
4175   case WM_MBUTTONUP:\r
4176   case WM_RBUTTONUP:\r
4177     ReleaseCapture();\r
4178     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4179     break;\r
4180  \r
4181   case WM_MBUTTONDOWN:\r
4182   case WM_RBUTTONDOWN:\r
4183     ErrorPopDown();\r
4184     ReleaseCapture();\r
4185     fromX = fromY = -1;\r
4186     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4187     dragInfo.start.x = dragInfo.start.y = -1;\r
4188     dragInfo.from = dragInfo.start;\r
4189     dragInfo.lastpos = dragInfo.pos;\r
4190     if (appData.highlightDragging) {\r
4191       ClearHighlights();\r
4192     }\r
4193     if(y == -2) {\r
4194       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4195       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4196           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4197       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4198           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4199       }\r
4200       break;\r
4201     }\r
4202     DrawPosition(TRUE, NULL);\r
4203 \r
4204     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4205     switch (menuNr) {\r
4206     case 0:\r
4207       if (message == WM_MBUTTONDOWN) {\r
4208         buttonCount = 3;  /* even if system didn't think so */\r
4209         if (wParam & MK_SHIFT) \r
4210           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4211         else\r
4212           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4213       } else { /* message == WM_RBUTTONDOWN */\r
4214         /* Just have one menu, on the right button.  Windows users don't\r
4215            think to try the middle one, and sometimes other software steals\r
4216            it, or it doesn't really exist. */\r
4217         if(gameInfo.variant != VariantShogi)\r
4218             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4219         else\r
4220             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4221       }\r
4222       break;\r
4223     case 2:\r
4224       SetCapture(hwndMain);
4225       break;\r
4226     case 1:\r
4227       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4228       SetupDropMenu(hmenu);\r
4229       MenuPopup(hwnd, pt, hmenu, -1);\r
4230     default:\r
4231       break;\r
4232     }\r
4233     break;\r
4234   }\r
4235 \r
4236   recursive--;\r
4237 }\r
4238 \r
4239 /* Preprocess messages for buttons in main window */\r
4240 LRESULT CALLBACK\r
4241 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4242 {\r
4243   int id = GetWindowLong(hwnd, GWL_ID);\r
4244   int i, dir;\r
4245 \r
4246   for (i=0; i<N_BUTTONS; i++) {\r
4247     if (buttonDesc[i].id == id) break;\r
4248   }\r
4249   if (i == N_BUTTONS) return 0;\r
4250   switch (message) {\r
4251   case WM_KEYDOWN:\r
4252     switch (wParam) {\r
4253     case VK_LEFT:\r
4254     case VK_RIGHT:\r
4255       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4256       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4257       return TRUE;\r
4258     }\r
4259     break;\r
4260   case WM_CHAR:\r
4261     switch (wParam) {\r
4262     case '\r':\r
4263       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4264       return TRUE;\r
4265     default:\r
4266       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4267         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4268         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4269         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4270         SetFocus(h);\r
4271         SendMessage(h, WM_CHAR, wParam, lParam);\r
4272         return TRUE;\r
4273       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4274         PopUpMoveDialog((char)wParam);\r
4275       }\r
4276       break;\r
4277     }\r
4278     break;\r
4279   }\r
4280   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4281 }\r
4282 \r
4283 /* Process messages for Promotion dialog box */\r
4284 LRESULT CALLBACK\r
4285 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4286 {\r
4287   char promoChar;\r
4288 \r
4289   switch (message) {\r
4290   case WM_INITDIALOG: /* message: initialize dialog box */\r
4291     /* Center the dialog over the application window */\r
4292     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4293     Translate(hDlg, DLG_PromotionKing);\r
4294     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4295       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4296        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4297        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4298                SW_SHOW : SW_HIDE);\r
4299     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4300     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4301        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4302          PieceToChar(WhiteAngel) != '~') ||\r
4303         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4304          PieceToChar(BlackAngel) != '~')   ) ?\r
4305                SW_SHOW : SW_HIDE);\r
4306     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4307        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4308          PieceToChar(WhiteMarshall) != '~') ||\r
4309         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4310          PieceToChar(BlackMarshall) != '~')   ) ?\r
4311                SW_SHOW : SW_HIDE);\r
4312     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4313     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4314        gameInfo.variant != VariantShogi ?\r
4315                SW_SHOW : SW_HIDE);\r
4316     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4317        gameInfo.variant != VariantShogi ?\r
4318                SW_SHOW : SW_HIDE);\r
4319     if(gameInfo.variant == VariantShogi) {\r
4320         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4321         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4322         SetWindowText(hDlg, "Promote?");\r
4323     }\r
4324     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4325        gameInfo.variant == VariantSuper ?\r
4326                SW_SHOW : SW_HIDE);\r
4327     return TRUE;\r
4328 \r
4329   case WM_COMMAND: /* message: received a command */\r
4330     switch (LOWORD(wParam)) {\r
4331     case IDCANCEL:\r
4332       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4333       ClearHighlights();\r
4334       DrawPosition(FALSE, NULL);\r
4335       return TRUE;\r
4336     case PB_King:\r
4337       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4338       break;\r
4339     case PB_Queen:\r
4340       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4341       break;\r
4342     case PB_Rook:\r
4343       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4344       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4345       break;\r
4346     case PB_Bishop:\r
4347       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4348       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4349       break;\r
4350     case PB_Chancellor:\r
4351       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4352       break;\r
4353     case PB_Archbishop:\r
4354       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4355       break;\r
4356     case PB_Knight:\r
4357       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4358       break;\r
4359     default:\r
4360       return FALSE;\r
4361     }\r
4362     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4363     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4364     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4365     fromX = fromY = -1;\r
4366     if (!appData.highlightLastMove) {\r
4367       ClearHighlights();\r
4368       DrawPosition(FALSE, NULL);\r
4369     }\r
4370     return TRUE;\r
4371   }\r
4372   return FALSE;\r
4373 }\r
4374 \r
4375 /* Pop up promotion dialog */\r
4376 VOID\r
4377 PromotionPopup(HWND hwnd)\r
4378 {\r
4379   FARPROC lpProc;\r
4380 \r
4381   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4382   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4383     hwnd, (DLGPROC)lpProc);\r
4384   FreeProcInstance(lpProc);\r
4385 }\r
4386 \r
4387 void\r
4388 PromotionPopUp()\r
4389 {\r
4390   DrawPosition(TRUE, NULL);\r
4391   PromotionPopup(hwndMain);\r
4392 }\r
4393 \r
4394 /* Toggle ShowThinking */\r
4395 VOID\r
4396 ToggleShowThinking()\r
4397 {\r
4398   appData.showThinking = !appData.showThinking;\r
4399   ShowThinkingEvent();\r
4400 }\r
4401 \r
4402 VOID\r
4403 LoadGameDialog(HWND hwnd, char* title)\r
4404 {\r
4405   UINT number = 0;\r
4406   FILE *f;\r
4407   char fileTitle[MSG_SIZ];\r
4408   f = OpenFileDialog(hwnd, "rb", "",\r
4409                      appData.oldSaveStyle ? "gam" : "pgn",\r
4410                      GAME_FILT,\r
4411                      title, &number, fileTitle, NULL);\r
4412   if (f != NULL) {\r
4413     cmailMsgLoaded = FALSE;\r
4414     if (number == 0) {\r
4415       int error = GameListBuild(f);\r
4416       if (error) {\r
4417         DisplayError(_("Cannot build game list"), error);\r
4418       } else if (!ListEmpty(&gameList) &&\r
4419                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4420         GameListPopUp(f, fileTitle);\r
4421         return;\r
4422       }\r
4423       GameListDestroy();\r
4424       number = 1;\r
4425     }\r
4426     LoadGame(f, number, fileTitle, FALSE);\r
4427   }\r
4428 }\r
4429 \r
4430 int get_term_width()\r
4431 {\r
4432     HDC hdc;\r
4433     TEXTMETRIC tm;\r
4434     RECT rc;\r
4435     HFONT hfont, hold_font;\r
4436     LOGFONT lf;\r
4437     HWND hText;\r
4438 \r
4439     if (hwndConsole)\r
4440         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4441     else\r
4442         return 79;\r
4443 \r
4444     // get the text metrics\r
4445     hdc = GetDC(hText);\r
4446     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4447     if (consoleCF.dwEffects & CFE_BOLD)\r
4448         lf.lfWeight = FW_BOLD;\r
4449     if (consoleCF.dwEffects & CFE_ITALIC)\r
4450         lf.lfItalic = TRUE;\r
4451     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4452         lf.lfStrikeOut = TRUE;\r
4453     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4454         lf.lfUnderline = TRUE;\r
4455     hfont = CreateFontIndirect(&lf);\r
4456     hold_font = SelectObject(hdc, hfont);\r
4457     GetTextMetrics(hdc, &tm);\r
4458     SelectObject(hdc, hold_font);\r
4459     DeleteObject(hfont);\r
4460     ReleaseDC(hText, hdc);\r
4461 \r
4462     // get the rectangle\r
4463     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4464 \r
4465     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4466 }\r
4467 \r
4468 void UpdateICSWidth(HWND hText)\r
4469 {\r
4470     LONG old_width, new_width;\r
4471 \r
4472     new_width = get_term_width(hText, FALSE);\r
4473     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4474     if (new_width != old_width)\r
4475     {\r
4476         ics_update_width(new_width);\r
4477         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4478     }\r
4479 }\r
4480 \r
4481 VOID\r
4482 ChangedConsoleFont()\r
4483 {\r
4484   CHARFORMAT cfmt;\r
4485   CHARRANGE tmpsel, sel;\r
4486   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4487   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4488   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4489   PARAFORMAT paraf;\r
4490 \r
4491   cfmt.cbSize = sizeof(CHARFORMAT);\r
4492   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4493     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4494                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4495   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4496    * size.  This was undocumented in the version of MSVC++ that I had\r
4497    * when I wrote the code, but is apparently documented now.\r
4498    */\r
4499   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4500   cfmt.bCharSet = f->lf.lfCharSet;\r
4501   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4502   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4503   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4504   /* Why are the following seemingly needed too? */\r
4505   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4506   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4507   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4508   tmpsel.cpMin = 0;\r
4509   tmpsel.cpMax = -1; /*999999?*/\r
4510   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4511   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4512   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4513    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4514    */\r
4515   paraf.cbSize = sizeof(paraf);\r
4516   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4517   paraf.dxStartIndent = 0;\r
4518   paraf.dxOffset = WRAP_INDENT;\r
4519   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4520   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4521   UpdateICSWidth(hText);\r
4522 }\r
4523 \r
4524 /*---------------------------------------------------------------------------*\\r
4525  *\r
4526  * Window Proc for main window\r
4527  *\r
4528 \*---------------------------------------------------------------------------*/\r
4529 \r
4530 /* Process messages for main window, etc. */\r
4531 LRESULT CALLBACK\r
4532 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4533 {\r
4534   FARPROC lpProc;\r
4535   int wmId, wmEvent;\r
4536   char *defName;\r
4537   FILE *f;\r
4538   UINT number;\r
4539   char fileTitle[MSG_SIZ];\r
4540   char buf[MSG_SIZ];\r
4541   static SnapData sd;\r
4542 \r
4543   switch (message) {\r
4544 \r
4545   case WM_PAINT: /* message: repaint portion of window */\r
4546     PaintProc(hwnd);\r
4547     break;\r
4548 \r
4549   case WM_ERASEBKGND:\r
4550     if (IsIconic(hwnd)) {\r
4551       /* Cheat; change the message */\r
4552       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4553     } else {\r
4554       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4555     }\r
4556     break;\r
4557 \r
4558   case WM_LBUTTONDOWN:\r
4559   case WM_MBUTTONDOWN:\r
4560   case WM_RBUTTONDOWN:\r
4561   case WM_LBUTTONUP:\r
4562   case WM_MBUTTONUP:\r
4563   case WM_RBUTTONUP:\r
4564   case WM_MOUSEMOVE:\r
4565   case WM_MOUSEWHEEL:\r
4566     MouseEvent(hwnd, message, wParam, lParam);\r
4567     break;\r
4568 \r
4569   JAWS_KB_NAVIGATION\r
4570 \r
4571   case WM_CHAR:\r
4572     \r
4573     JAWS_ALT_INTERCEPT\r
4574 \r
4575     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4576         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4577         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4578         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4579         SetFocus(h);\r
4580         SendMessage(h, message, wParam, lParam);\r
4581     } else if(lParam != KF_REPEAT) {\r
4582         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4583                 PopUpMoveDialog((char)wParam);\r
4584         } else if((char)wParam == 003) CopyGameToClipboard();\r
4585          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4586     }\r
4587 \r
4588     break;\r
4589 \r
4590   case WM_PALETTECHANGED:\r
4591     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4592       int nnew;\r
4593       HDC hdc = GetDC(hwndMain);\r
4594       SelectPalette(hdc, hPal, TRUE);\r
4595       nnew = RealizePalette(hdc);\r
4596       if (nnew > 0) {\r
4597         paletteChanged = TRUE;\r
4598         InvalidateRect(hwnd, &boardRect, FALSE);\r
4599       }\r
4600       ReleaseDC(hwnd, hdc);\r
4601     }\r
4602     break;\r
4603 \r
4604   case WM_QUERYNEWPALETTE:\r
4605     if (!appData.monoMode /*&& paletteChanged*/) {\r
4606       int nnew;\r
4607       HDC hdc = GetDC(hwndMain);\r
4608       paletteChanged = FALSE;\r
4609       SelectPalette(hdc, hPal, FALSE);\r
4610       nnew = RealizePalette(hdc);\r
4611       if (nnew > 0) {\r
4612         InvalidateRect(hwnd, &boardRect, FALSE);\r
4613       }\r
4614       ReleaseDC(hwnd, hdc);\r
4615       return TRUE;\r
4616     }\r
4617     return FALSE;\r
4618 \r
4619   case WM_COMMAND: /* message: command from application menu */\r
4620     wmId    = LOWORD(wParam);\r
4621     wmEvent = HIWORD(wParam);\r
4622 \r
4623     switch (wmId) {\r
4624     case IDM_NewGame:\r
4625       ResetGameEvent();\r
4626       SAY("new game enter a move to play against the computer with white");\r
4627       break;\r
4628 \r
4629     case IDM_NewGameFRC:\r
4630       if( NewGameFRC() == 0 ) {\r
4631         ResetGameEvent();\r
4632       }\r
4633       break;\r
4634 \r
4635     case IDM_NewVariant:\r
4636       NewVariantPopup(hwnd);\r
4637       break;\r
4638 \r
4639     case IDM_LoadGame:\r
4640       LoadGameDialog(hwnd, _("Load Game from File"));\r
4641       break;\r
4642 \r
4643     case IDM_LoadNextGame:\r
4644       ReloadGame(1);\r
4645       break;\r
4646 \r
4647     case IDM_LoadPrevGame:\r
4648       ReloadGame(-1);\r
4649       break;\r
4650 \r
4651     case IDM_ReloadGame:\r
4652       ReloadGame(0);\r
4653       break;\r
4654 \r
4655     case IDM_LoadPosition:\r
4656       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4657         Reset(FALSE, TRUE);\r
4658       }\r
4659       number = 1;\r
4660       f = OpenFileDialog(hwnd, "rb", "",\r
4661                          appData.oldSaveStyle ? "pos" : "fen",\r
4662                          POSITION_FILT,\r
4663                          _("Load Position from File"), &number, fileTitle, NULL);\r
4664       if (f != NULL) {\r
4665         LoadPosition(f, number, fileTitle);\r
4666       }\r
4667       break;\r
4668 \r
4669     case IDM_LoadNextPosition:\r
4670       ReloadPosition(1);\r
4671       break;\r
4672 \r
4673     case IDM_LoadPrevPosition:\r
4674       ReloadPosition(-1);\r
4675       break;\r
4676 \r
4677     case IDM_ReloadPosition:\r
4678       ReloadPosition(0);\r
4679       break;\r
4680 \r
4681     case IDM_SaveGame:\r
4682       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4683       f = OpenFileDialog(hwnd, "a", defName,\r
4684                          appData.oldSaveStyle ? "gam" : "pgn",\r
4685                          GAME_FILT,\r
4686                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4687       if (f != NULL) {\r
4688         SaveGame(f, 0, "");\r
4689       }\r
4690       break;\r
4691 \r
4692     case IDM_SavePosition:\r
4693       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4694       f = OpenFileDialog(hwnd, "a", defName,\r
4695                          appData.oldSaveStyle ? "pos" : "fen",\r
4696                          POSITION_FILT,\r
4697                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4698       if (f != NULL) {\r
4699         SavePosition(f, 0, "");\r
4700       }\r
4701       break;\r
4702 \r
4703     case IDM_SaveDiagram:\r
4704       defName = "diagram";\r
4705       f = OpenFileDialog(hwnd, "wb", defName,\r
4706                          "bmp",\r
4707                          DIAGRAM_FILT,\r
4708                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4709       if (f != NULL) {\r
4710         SaveDiagram(f);\r
4711       }\r
4712       break;\r
4713 \r
4714     case IDM_CopyGame:\r
4715       CopyGameToClipboard();\r
4716       break;\r
4717 \r
4718     case IDM_PasteGame:\r
4719       PasteGameFromClipboard();\r
4720       break;\r
4721 \r
4722     case IDM_CopyGameListToClipboard:\r
4723       CopyGameListToClipboard();\r
4724       break;\r
4725 \r
4726     /* [AS] Autodetect FEN or PGN data */\r
4727     case IDM_PasteAny:\r
4728       PasteGameOrFENFromClipboard();\r
4729       break;\r
4730 \r
4731     /* [AS] Move history */\r
4732     case IDM_ShowMoveHistory:\r
4733         if( MoveHistoryIsUp() ) {\r
4734             MoveHistoryPopDown();\r
4735         }\r
4736         else {\r
4737             MoveHistoryPopUp();\r
4738         }\r
4739         break;\r
4740 \r
4741     /* [AS] Eval graph */\r
4742     case IDM_ShowEvalGraph:\r
4743         if( EvalGraphIsUp() ) {\r
4744             EvalGraphPopDown();\r
4745         }\r
4746         else {\r
4747             EvalGraphPopUp();\r
4748             SetFocus(hwndMain);\r
4749         }\r
4750         break;\r
4751 \r
4752     /* [AS] Engine output */\r
4753     case IDM_ShowEngineOutput:\r
4754         if( EngineOutputIsUp() ) {\r
4755             EngineOutputPopDown();\r
4756         }\r
4757         else {\r
4758             EngineOutputPopUp();\r
4759         }\r
4760         break;\r
4761 \r
4762     /* [AS] User adjudication */\r
4763     case IDM_UserAdjudication_White:\r
4764         UserAdjudicationEvent( +1 );\r
4765         break;\r
4766 \r
4767     case IDM_UserAdjudication_Black:\r
4768         UserAdjudicationEvent( -1 );\r
4769         break;\r
4770 \r
4771     case IDM_UserAdjudication_Draw:\r
4772         UserAdjudicationEvent( 0 );\r
4773         break;\r
4774 \r
4775     /* [AS] Game list options dialog */\r
4776     case IDM_GameListOptions:\r
4777       GameListOptions();\r
4778       break;\r
4779 \r
4780     case IDM_NewChat:\r
4781       ChatPopUp(NULL);\r
4782       break;\r
4783 \r
4784     case IDM_CopyPosition:\r
4785       CopyFENToClipboard();\r
4786       break;\r
4787 \r
4788     case IDM_PastePosition:\r
4789       PasteFENFromClipboard();\r
4790       break;\r
4791 \r
4792     case IDM_MailMove:\r
4793       MailMoveEvent();\r
4794       break;\r
4795 \r
4796     case IDM_ReloadCMailMsg:\r
4797       Reset(TRUE, TRUE);\r
4798       ReloadCmailMsgEvent(FALSE);\r
4799       break;\r
4800 \r
4801     case IDM_Minimize:\r
4802       ShowWindow(hwnd, SW_MINIMIZE);\r
4803       break;\r
4804 \r
4805     case IDM_Exit:\r
4806       ExitEvent(0);\r
4807       break;\r
4808 \r
4809     case IDM_MachineWhite:\r
4810       MachineWhiteEvent();\r
4811       /*\r
4812        * refresh the tags dialog only if it's visible\r
4813        */\r
4814       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4815           char *tags;\r
4816           tags = PGNTags(&gameInfo);\r
4817           TagsPopUp(tags, CmailMsg());\r
4818           free(tags);\r
4819       }\r
4820       SAY("computer starts playing white");\r
4821       break;\r
4822 \r
4823     case IDM_MachineBlack:\r
4824       MachineBlackEvent();\r
4825       /*\r
4826        * refresh the tags dialog only if it's visible\r
4827        */\r
4828       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4829           char *tags;\r
4830           tags = PGNTags(&gameInfo);\r
4831           TagsPopUp(tags, CmailMsg());\r
4832           free(tags);\r
4833       }\r
4834       SAY("computer starts playing black");\r
4835       break;\r
4836 \r
4837     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4838       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4839         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4840       }\r
4841       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4842       appData.matchGames = appData.defaultMatchGames;\r
4843       matchGame = 1;\r
4844       first.matchWins = second.matchWins = 0;\r
4845 \r
4846     case IDM_TwoMachines:\r
4847       TwoMachinesEvent();\r
4848       /*\r
4849        * refresh the tags dialog only if it's visible\r
4850        */\r
4851       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4852           char *tags;\r
4853           tags = PGNTags(&gameInfo);\r
4854           TagsPopUp(tags, CmailMsg());\r
4855           free(tags);\r
4856       }\r
4857       SAY("computer starts playing both sides");\r
4858       break;\r
4859 \r
4860     case IDM_AnalysisMode:\r
4861       if (!first.analysisSupport) {\r
4862         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4863         DisplayError(buf, 0);\r
4864       } else {\r
4865         SAY("analyzing current position");\r
4866         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4867         if (appData.icsActive) {\r
4868                if (gameMode != IcsObserving) {\r
4869                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4870                        DisplayError(buf, 0);\r
4871                        /* secure check */\r
4872                        if (appData.icsEngineAnalyze) {\r
4873                                if (appData.debugMode) \r
4874                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4875                                ExitAnalyzeMode();\r
4876                                ModeHighlight();\r
4877                                break;\r
4878                        }\r
4879                        break;\r
4880                } else {\r
4881                        /* if enable, user want disable icsEngineAnalyze */\r
4882                        if (appData.icsEngineAnalyze) {\r
4883                                ExitAnalyzeMode();\r
4884                                ModeHighlight();\r
4885                                break;\r
4886                        }\r
4887                        appData.icsEngineAnalyze = TRUE;\r
4888                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4889                }\r
4890         } \r
4891         if (!appData.showThinking) ToggleShowThinking();\r
4892         AnalyzeModeEvent();\r
4893       }\r
4894       break;\r
4895 \r
4896     case IDM_AnalyzeFile:\r
4897       if (!first.analysisSupport) {\r
4898         char buf[MSG_SIZ];\r
4899           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4900         DisplayError(buf, 0);\r
4901       } else {\r
4902         if (!appData.showThinking) ToggleShowThinking();\r
4903         AnalyzeFileEvent();\r
4904         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4905         AnalysisPeriodicEvent(1);\r
4906       }\r
4907       break;\r
4908 \r
4909     case IDM_IcsClient:\r
4910       IcsClientEvent();\r
4911       break;\r
4912 \r
4913     case IDM_EditGame:\r
4914     case IDM_EditGame2:\r
4915       EditGameEvent();\r
4916       SAY("edit game");\r
4917       break;\r
4918 \r
4919     case IDM_EditPosition:\r
4920     case IDM_EditPosition2:\r
4921       EditPositionEvent();\r
4922       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4923       break;\r
4924 \r
4925     case IDM_Training:\r
4926       TrainingEvent();\r
4927       break;\r
4928 \r
4929     case IDM_ShowGameList:\r
4930       ShowGameListProc();\r
4931       break;\r
4932 \r
4933     case IDM_EditProgs1:\r
4934       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4935       break;\r
4936 \r
4937     case IDM_EditProgs2:\r
4938       EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4939       break;\r
4940 \r
4941     case IDM_EditServers:\r
4942       EditTagsPopUp(icsNames, &icsNames);\r
4943       break;\r
4944 \r
4945     case IDM_EditTags:\r
4946     case IDM_Tags:\r
4947       EditTagsProc();\r
4948       break;\r
4949 \r
4950     case IDM_EditComment:\r
4951     case IDM_Comment:\r
4952       if (commentUp && editComment) {\r
4953         CommentPopDown();\r
4954       } else {\r
4955         EditCommentEvent();\r
4956       }\r
4957       break;\r
4958 \r
4959     case IDM_Pause:\r
4960       PauseEvent();\r
4961       break;\r
4962 \r
4963     case IDM_Accept:\r
4964       AcceptEvent();\r
4965       break;\r
4966 \r
4967     case IDM_Decline:\r
4968       DeclineEvent();\r
4969       break;\r
4970 \r
4971     case IDM_Rematch:\r
4972       RematchEvent();\r
4973       break;\r
4974 \r
4975     case IDM_CallFlag:\r
4976       CallFlagEvent();\r
4977       break;\r
4978 \r
4979     case IDM_Draw:\r
4980       DrawEvent();\r
4981       break;\r
4982 \r
4983     case IDM_Adjourn:\r
4984       AdjournEvent();\r
4985       break;\r
4986 \r
4987     case IDM_Abort:\r
4988       AbortEvent();\r
4989       break;\r
4990 \r
4991     case IDM_Resign:\r
4992       ResignEvent();\r
4993       break;\r
4994 \r
4995     case IDM_StopObserving:\r
4996       StopObservingEvent();\r
4997       break;\r
4998 \r
4999     case IDM_StopExamining:\r
5000       StopExaminingEvent();\r
5001       break;\r
5002 \r
5003     case IDM_Upload:\r
5004       UploadGameEvent();\r
5005       break;\r
5006 \r
5007     case IDM_TypeInMove:\r
5008       PopUpMoveDialog('\000');\r
5009       break;\r
5010 \r
5011     case IDM_TypeInName:\r
5012       PopUpNameDialog('\000');\r
5013       break;\r
5014 \r
5015     case IDM_Backward:\r
5016       BackwardEvent();\r
5017       SetFocus(hwndMain);\r
5018       break;\r
5019 \r
5020     JAWS_MENU_ITEMS\r
5021 \r
5022     case IDM_Forward:\r
5023       ForwardEvent();\r
5024       SetFocus(hwndMain);\r
5025       break;\r
5026 \r
5027     case IDM_ToStart:\r
5028       ToStartEvent();\r
5029       SetFocus(hwndMain);\r
5030       break;\r
5031 \r
5032     case IDM_ToEnd:\r
5033       ToEndEvent();\r
5034       SetFocus(hwndMain);\r
5035       break;\r
5036 \r
5037     case IDM_Revert:\r
5038       RevertEvent(FALSE);\r
5039       break;\r
5040 \r
5041     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5042       RevertEvent(TRUE);\r
5043       break;\r
5044 \r
5045     case IDM_TruncateGame:\r
5046       TruncateGameEvent();\r
5047       break;\r
5048 \r
5049     case IDM_MoveNow:\r
5050       MoveNowEvent();\r
5051       break;\r
5052 \r
5053     case IDM_RetractMove:\r
5054       RetractMoveEvent();\r
5055       break;\r
5056 \r
5057     case IDM_FlipView:\r
5058       flipView = !flipView;\r
5059       DrawPosition(FALSE, NULL);\r
5060       break;\r
5061 \r
5062     case IDM_FlipClock:\r
5063       flipClock = !flipClock;\r
5064       DisplayBothClocks();\r
5065       DisplayLogos();\r
5066       break;\r
5067 \r
5068     case IDM_MuteSounds:\r
5069       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5070       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5071                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5072       break;\r
5073 \r
5074     case IDM_GeneralOptions:\r
5075       GeneralOptionsPopup(hwnd);\r
5076       DrawPosition(TRUE, NULL);\r
5077       break;\r
5078 \r
5079     case IDM_BoardOptions:\r
5080       BoardOptionsPopup(hwnd);\r
5081       break;\r
5082 \r
5083     case IDM_EnginePlayOptions:\r
5084       EnginePlayOptionsPopup(hwnd);\r
5085       break;\r
5086 \r
5087     case IDM_Engine1Options:\r
5088       EngineOptionsPopup(hwnd, &first);\r
5089       break;\r
5090 \r
5091     case IDM_Engine2Options:\r
5092       savedHwnd = hwnd;\r
5093       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5094       EngineOptionsPopup(hwnd, &second);\r
5095       break;\r
5096 \r
5097     case IDM_OptionsUCI:\r
5098       UciOptionsPopup(hwnd);\r
5099       break;\r
5100 \r
5101     case IDM_IcsOptions:\r
5102       IcsOptionsPopup(hwnd);\r
5103       break;\r
5104 \r
5105     case IDM_Fonts:\r
5106       FontsOptionsPopup(hwnd);\r
5107       break;\r
5108 \r
5109     case IDM_Sounds:\r
5110       SoundOptionsPopup(hwnd);\r
5111       break;\r
5112 \r
5113     case IDM_CommPort:\r
5114       CommPortOptionsPopup(hwnd);\r
5115       break;\r
5116 \r
5117     case IDM_LoadOptions:\r
5118       LoadOptionsPopup(hwnd);\r
5119       break;\r
5120 \r
5121     case IDM_SaveOptions:\r
5122       SaveOptionsPopup(hwnd);\r
5123       break;\r
5124 \r
5125     case IDM_TimeControl:\r
5126       TimeControlOptionsPopup(hwnd);\r
5127       break;\r
5128 \r
5129     case IDM_SaveSettings:\r
5130       SaveSettings(settingsFileName);\r
5131       break;\r
5132 \r
5133     case IDM_SaveSettingsOnExit:\r
5134       saveSettingsOnExit = !saveSettingsOnExit;\r
5135       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5136                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5137                                          MF_CHECKED : MF_UNCHECKED));\r
5138       break;\r
5139 \r
5140     case IDM_Hint:\r
5141       HintEvent();\r
5142       break;\r
5143 \r
5144     case IDM_Book:\r
5145       BookEvent();\r
5146       break;\r
5147 \r
5148     case IDM_AboutGame:\r
5149       AboutGameEvent();\r
5150       break;\r
5151 \r
5152     case IDM_Debug:\r
5153       appData.debugMode = !appData.debugMode;\r
5154       if (appData.debugMode) {\r
5155         char dir[MSG_SIZ];\r
5156         GetCurrentDirectory(MSG_SIZ, dir);\r
5157         SetCurrentDirectory(installDir);\r
5158         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5159         SetCurrentDirectory(dir);\r
5160         setbuf(debugFP, NULL);\r
5161       } else {\r
5162         fclose(debugFP);\r
5163         debugFP = NULL;\r
5164       }\r
5165       break;\r
5166 \r
5167     case IDM_HELPCONTENTS:\r
5168       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5169           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5170           MessageBox (GetFocus(),\r
5171                     _("Unable to activate help"),\r
5172                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5173       }\r
5174       break;\r
5175 \r
5176     case IDM_HELPSEARCH:\r
5177         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5178             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5179         MessageBox (GetFocus(),\r
5180                     _("Unable to activate help"),\r
5181                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5182       }\r
5183       break;\r
5184 \r
5185     case IDM_HELPHELP:\r
5186       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5187         MessageBox (GetFocus(),\r
5188                     _("Unable to activate help"),\r
5189                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5190       }\r
5191       break;\r
5192 \r
5193     case IDM_ABOUT:\r
5194       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5195       DialogBox(hInst, \r
5196         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5197         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5198       FreeProcInstance(lpProc);\r
5199       break;\r
5200 \r
5201     case IDM_DirectCommand1:\r
5202       AskQuestionEvent(_("Direct Command"),\r
5203                        _("Send to chess program:"), "", "1");\r
5204       break;\r
5205     case IDM_DirectCommand2:\r
5206       AskQuestionEvent(_("Direct Command"),\r
5207                        _("Send to second chess program:"), "", "2");\r
5208       break;\r
5209 \r
5210     case EP_WhitePawn:\r
5211       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5212       fromX = fromY = -1;\r
5213       break;\r
5214 \r
5215     case EP_WhiteKnight:\r
5216       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5217       fromX = fromY = -1;\r
5218       break;\r
5219 \r
5220     case EP_WhiteBishop:\r
5221       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5222       fromX = fromY = -1;\r
5223       break;\r
5224 \r
5225     case EP_WhiteRook:\r
5226       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5227       fromX = fromY = -1;\r
5228       break;\r
5229 \r
5230     case EP_WhiteQueen:\r
5231       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5232       fromX = fromY = -1;\r
5233       break;\r
5234 \r
5235     case EP_WhiteFerz:\r
5236       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5237       fromX = fromY = -1;\r
5238       break;\r
5239 \r
5240     case EP_WhiteWazir:\r
5241       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5242       fromX = fromY = -1;\r
5243       break;\r
5244 \r
5245     case EP_WhiteAlfil:\r
5246       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5247       fromX = fromY = -1;\r
5248       break;\r
5249 \r
5250     case EP_WhiteCannon:\r
5251       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5252       fromX = fromY = -1;\r
5253       break;\r
5254 \r
5255     case EP_WhiteCardinal:\r
5256       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5257       fromX = fromY = -1;\r
5258       break;\r
5259 \r
5260     case EP_WhiteMarshall:\r
5261       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5262       fromX = fromY = -1;\r
5263       break;\r
5264 \r
5265     case EP_WhiteKing:\r
5266       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5267       fromX = fromY = -1;\r
5268       break;\r
5269 \r
5270     case EP_BlackPawn:\r
5271       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5272       fromX = fromY = -1;\r
5273       break;\r
5274 \r
5275     case EP_BlackKnight:\r
5276       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5277       fromX = fromY = -1;\r
5278       break;\r
5279 \r
5280     case EP_BlackBishop:\r
5281       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5282       fromX = fromY = -1;\r
5283       break;\r
5284 \r
5285     case EP_BlackRook:\r
5286       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5287       fromX = fromY = -1;\r
5288       break;\r
5289 \r
5290     case EP_BlackQueen:\r
5291       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5292       fromX = fromY = -1;\r
5293       break;\r
5294 \r
5295     case EP_BlackFerz:\r
5296       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5297       fromX = fromY = -1;\r
5298       break;\r
5299 \r
5300     case EP_BlackWazir:\r
5301       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5302       fromX = fromY = -1;\r
5303       break;\r
5304 \r
5305     case EP_BlackAlfil:\r
5306       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5307       fromX = fromY = -1;\r
5308       break;\r
5309 \r
5310     case EP_BlackCannon:\r
5311       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5312       fromX = fromY = -1;\r
5313       break;\r
5314 \r
5315     case EP_BlackCardinal:\r
5316       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5317       fromX = fromY = -1;\r
5318       break;\r
5319 \r
5320     case EP_BlackMarshall:\r
5321       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5322       fromX = fromY = -1;\r
5323       break;\r
5324 \r
5325     case EP_BlackKing:\r
5326       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5327       fromX = fromY = -1;\r
5328       break;\r
5329 \r
5330     case EP_EmptySquare:\r
5331       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5332       fromX = fromY = -1;\r
5333       break;\r
5334 \r
5335     case EP_ClearBoard:\r
5336       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5337       fromX = fromY = -1;\r
5338       break;\r
5339 \r
5340     case EP_White:\r
5341       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5342       fromX = fromY = -1;\r
5343       break;\r
5344 \r
5345     case EP_Black:\r
5346       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5347       fromX = fromY = -1;\r
5348       break;\r
5349 \r
5350     case EP_Promote:\r
5351       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5352       fromX = fromY = -1;\r
5353       break;\r
5354 \r
5355     case EP_Demote:\r
5356       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5357       fromX = fromY = -1;\r
5358       break;\r
5359 \r
5360     case DP_Pawn:\r
5361       DropMenuEvent(WhitePawn, fromX, fromY);\r
5362       fromX = fromY = -1;\r
5363       break;\r
5364 \r
5365     case DP_Knight:\r
5366       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5367       fromX = fromY = -1;\r
5368       break;\r
5369 \r
5370     case DP_Bishop:\r
5371       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5372       fromX = fromY = -1;\r
5373       break;\r
5374 \r
5375     case DP_Rook:\r
5376       DropMenuEvent(WhiteRook, fromX, fromY);\r
5377       fromX = fromY = -1;\r
5378       break;\r
5379 \r
5380     case DP_Queen:\r
5381       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5382       fromX = fromY = -1;\r
5383       break;\r
5384 \r
5385     case IDM_English:\r
5386       barbaric = 0;\r
5387       TranslateMenus(0);\r
5388       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5389       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5390       lastChecked = wmId;\r
5391       break;\r
5392 \r
5393     default:\r
5394       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5395           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5396           TranslateMenus(0);\r
5397           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5398           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5399           lastChecked = wmId;\r
5400           break;\r
5401       }\r
5402       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5403     }\r
5404     break;\r
5405 \r
5406   case WM_TIMER:\r
5407     switch (wParam) {\r
5408     case CLOCK_TIMER_ID:\r
5409       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5410       clockTimerEvent = 0;\r
5411       DecrementClocks(); /* call into back end */\r
5412       break;\r
5413     case LOAD_GAME_TIMER_ID:\r
5414       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5415       loadGameTimerEvent = 0;\r
5416       AutoPlayGameLoop(); /* call into back end */\r
5417       break;\r
5418     case ANALYSIS_TIMER_ID:\r
5419       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5420                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5421         AnalysisPeriodicEvent(0);\r
5422       } else {\r
5423         KillTimer(hwnd, analysisTimerEvent);\r
5424         analysisTimerEvent = 0;\r
5425       }\r
5426       break;\r
5427     case DELAYED_TIMER_ID:\r
5428       KillTimer(hwnd, delayedTimerEvent);\r
5429       delayedTimerEvent = 0;\r
5430       delayedTimerCallback();\r
5431       break;\r
5432     }\r
5433     break;\r
5434 \r
5435   case WM_USER_Input:\r
5436     InputEvent(hwnd, message, wParam, lParam);\r
5437     break;\r
5438 \r
5439   /* [AS] Also move "attached" child windows */\r
5440   case WM_WINDOWPOSCHANGING:\r
5441 \r
5442     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5443         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5444 \r
5445         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5446             /* Window is moving */\r
5447             RECT rcMain;\r
5448 \r
5449 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5450             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5451             rcMain.right  = wpMain.x + wpMain.width;\r
5452             rcMain.top    = wpMain.y;\r
5453             rcMain.bottom = wpMain.y + wpMain.height;\r
5454             \r
5455             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5456             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5457             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5458             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5459             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5460             wpMain.x = lpwp->x;\r
5461             wpMain.y = lpwp->y;\r
5462         }\r
5463     }\r
5464     break;\r
5465 \r
5466   /* [AS] Snapping */\r
5467   case WM_ENTERSIZEMOVE:\r
5468     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5469     if (hwnd == hwndMain) {\r
5470       doingSizing = TRUE;\r
5471       lastSizing = 0;\r
5472     }\r
5473     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5474     break;\r
5475 \r
5476   case WM_SIZING:\r
5477     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5478     if (hwnd == hwndMain) {\r
5479       lastSizing = wParam;\r
5480     }\r
5481     break;\r
5482 \r
5483   case WM_MOVING:\r
5484     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5485       return OnMoving( &sd, hwnd, wParam, lParam );\r
5486 \r
5487   case WM_EXITSIZEMOVE:\r
5488     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5489     if (hwnd == hwndMain) {\r
5490       RECT client;\r
5491       doingSizing = FALSE;\r
5492       InvalidateRect(hwnd, &boardRect, FALSE);\r
5493       GetClientRect(hwnd, &client);\r
5494       ResizeBoard(client.right, client.bottom, lastSizing);\r
5495       lastSizing = 0;\r
5496       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5497     }\r
5498     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5499     break;\r
5500 \r
5501   case WM_DESTROY: /* message: window being destroyed */\r
5502     PostQuitMessage(0);\r
5503     break;\r
5504 \r
5505   case WM_CLOSE:\r
5506     if (hwnd == hwndMain) {\r
5507       ExitEvent(0);\r
5508     }\r
5509     break;\r
5510 \r
5511   default:      /* Passes it on if unprocessed */\r
5512     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5513   }\r
5514   return 0;\r
5515 }\r
5516 \r
5517 /*---------------------------------------------------------------------------*\\r
5518  *\r
5519  * Misc utility routines\r
5520  *\r
5521 \*---------------------------------------------------------------------------*/\r
5522 \r
5523 /*\r
5524  * Decent random number generator, at least not as bad as Windows\r
5525  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5526  */\r
5527 unsigned int randstate;\r
5528 \r
5529 int\r
5530 myrandom(void)\r
5531 {\r
5532   randstate = randstate * 1664525 + 1013904223;\r
5533   return (int) randstate & 0x7fffffff;\r
5534 }\r
5535 \r
5536 void\r
5537 mysrandom(unsigned int seed)\r
5538 {\r
5539   randstate = seed;\r
5540 }\r
5541 \r
5542 \r
5543 /* \r
5544  * returns TRUE if user selects a different color, FALSE otherwise \r
5545  */\r
5546 \r
5547 BOOL\r
5548 ChangeColor(HWND hwnd, COLORREF *which)\r
5549 {\r
5550   static BOOL firstTime = TRUE;\r
5551   static DWORD customColors[16];\r
5552   CHOOSECOLOR cc;\r
5553   COLORREF newcolor;\r
5554   int i;\r
5555   ColorClass ccl;\r
5556 \r
5557   if (firstTime) {\r
5558     /* Make initial colors in use available as custom colors */\r
5559     /* Should we put the compiled-in defaults here instead? */\r
5560     i = 0;\r
5561     customColors[i++] = lightSquareColor & 0xffffff;\r
5562     customColors[i++] = darkSquareColor & 0xffffff;\r
5563     customColors[i++] = whitePieceColor & 0xffffff;\r
5564     customColors[i++] = blackPieceColor & 0xffffff;\r
5565     customColors[i++] = highlightSquareColor & 0xffffff;\r
5566     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5567 \r
5568     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5569       customColors[i++] = textAttribs[ccl].color;\r
5570     }\r
5571     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5572     firstTime = FALSE;\r
5573   }\r
5574 \r
5575   cc.lStructSize = sizeof(cc);\r
5576   cc.hwndOwner = hwnd;\r
5577   cc.hInstance = NULL;\r
5578   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5579   cc.lpCustColors = (LPDWORD) customColors;\r
5580   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5581 \r
5582   if (!ChooseColor(&cc)) return FALSE;\r
5583 \r
5584   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5585   if (newcolor == *which) return FALSE;\r
5586   *which = newcolor;\r
5587   return TRUE;\r
5588 \r
5589   /*\r
5590   InitDrawingColors();\r
5591   InvalidateRect(hwnd, &boardRect, FALSE);\r
5592   */\r
5593 }\r
5594 \r
5595 BOOLEAN\r
5596 MyLoadSound(MySound *ms)\r
5597 {\r
5598   BOOL ok = FALSE;\r
5599   struct stat st;\r
5600   FILE *f;\r
5601 \r
5602   if (ms->data) free(ms->data);\r
5603   ms->data = NULL;\r
5604 \r
5605   switch (ms->name[0]) {\r
5606   case NULLCHAR:\r
5607     /* Silence */\r
5608     ok = TRUE;\r
5609     break;\r
5610   case '$':\r
5611     /* System sound from Control Panel.  Don't preload here. */\r
5612     ok = TRUE;\r
5613     break;\r
5614   case '!':\r
5615     if (ms->name[1] == NULLCHAR) {\r
5616       /* "!" alone = silence */\r
5617       ok = TRUE;\r
5618     } else {\r
5619       /* Builtin wave resource.  Error if not found. */\r
5620       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5621       if (h == NULL) break;\r
5622       ms->data = (void *)LoadResource(hInst, h);\r
5623       if (h == NULL) break;\r
5624       ok = TRUE;\r
5625     }\r
5626     break;\r
5627   default:\r
5628     /* .wav file.  Error if not found. */\r
5629     f = fopen(ms->name, "rb");\r
5630     if (f == NULL) break;\r
5631     if (fstat(fileno(f), &st) < 0) break;\r
5632     ms->data = malloc(st.st_size);\r
5633     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5634     fclose(f);\r
5635     ok = TRUE;\r
5636     break;\r
5637   }\r
5638   if (!ok) {\r
5639     char buf[MSG_SIZ];\r
5640       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5641     DisplayError(buf, GetLastError());\r
5642   }\r
5643   return ok;\r
5644 }\r
5645 \r
5646 BOOLEAN\r
5647 MyPlaySound(MySound *ms)\r
5648 {\r
5649   BOOLEAN ok = FALSE;\r
5650 \r
5651   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5652   switch (ms->name[0]) {\r
5653   case NULLCHAR:\r
5654         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5655     /* Silence */\r
5656     ok = TRUE;\r
5657     break;\r
5658   case '$':\r
5659     /* System sound from Control Panel (deprecated feature).\r
5660        "$" alone or an unset sound name gets default beep (still in use). */\r
5661     if (ms->name[1]) {\r
5662       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5663     }\r
5664     if (!ok) ok = MessageBeep(MB_OK);\r
5665     break; \r
5666   case '!':\r
5667     /* Builtin wave resource, or "!" alone for silence */\r
5668     if (ms->name[1]) {\r
5669       if (ms->data == NULL) return FALSE;\r
5670       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5671     } else {\r
5672       ok = TRUE;\r
5673     }\r
5674     break;\r
5675   default:\r
5676     /* .wav file.  Error if not found. */\r
5677     if (ms->data == NULL) return FALSE;\r
5678     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5679     break;\r
5680   }\r
5681   /* Don't print an error: this can happen innocently if the sound driver\r
5682      is busy; for instance, if another instance of WinBoard is playing\r
5683      a sound at about the same time. */\r
5684   return ok;\r
5685 }\r
5686 \r
5687 \r
5688 LRESULT CALLBACK\r
5689 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5690 {\r
5691   BOOL ok;\r
5692   OPENFILENAME *ofn;\r
5693   static UINT *number; /* gross that this is static */\r
5694 \r
5695   switch (message) {\r
5696   case WM_INITDIALOG: /* message: initialize dialog box */\r
5697     /* Center the dialog over the application window */\r
5698     ofn = (OPENFILENAME *) lParam;\r
5699     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5700       number = (UINT *) ofn->lCustData;\r
5701       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5702     } else {\r
5703       number = NULL;\r
5704     }\r
5705     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5706     Translate(hDlg, 1536);\r
5707     return FALSE;  /* Allow for further processing */\r
5708 \r
5709   case WM_COMMAND:\r
5710     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5711       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5712     }\r
5713     return FALSE;  /* Allow for further processing */\r
5714   }\r
5715   return FALSE;\r
5716 }\r
5717 \r
5718 UINT APIENTRY\r
5719 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5720 {\r
5721   static UINT *number;\r
5722   OPENFILENAME *ofname;\r
5723   OFNOTIFY *ofnot;\r
5724   switch (uiMsg) {\r
5725   case WM_INITDIALOG:\r
5726     Translate(hdlg, DLG_IndexNumber);\r
5727     ofname = (OPENFILENAME *)lParam;\r
5728     number = (UINT *)(ofname->lCustData);\r
5729     break;\r
5730   case WM_NOTIFY:\r
5731     ofnot = (OFNOTIFY *)lParam;\r
5732     if (ofnot->hdr.code == CDN_FILEOK) {\r
5733       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5734     }\r
5735     break;\r
5736   }\r
5737   return 0;\r
5738 }\r
5739 \r
5740 \r
5741 FILE *\r
5742 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5743                char *nameFilt, char *dlgTitle, UINT *number,\r
5744                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5745 {\r
5746   OPENFILENAME openFileName;\r
5747   char buf1[MSG_SIZ];\r
5748   FILE *f;\r
5749 \r
5750   if (fileName == NULL) fileName = buf1;\r
5751   if (defName == NULL) {\r
5752     safeStrCpy(fileName, "*.", 3 );\r
5753     strcat(fileName, defExt);\r
5754   } else {\r
5755     safeStrCpy(fileName, defName, MSG_SIZ );\r
5756   }\r
5757     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5758   if (number) *number = 0;\r
5759 \r
5760   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5761   openFileName.hwndOwner         = hwnd;\r
5762   openFileName.hInstance         = (HANDLE) hInst;\r
5763   openFileName.lpstrFilter       = nameFilt;\r
5764   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5765   openFileName.nMaxCustFilter    = 0L;\r
5766   openFileName.nFilterIndex      = 1L;\r
5767   openFileName.lpstrFile         = fileName;\r
5768   openFileName.nMaxFile          = MSG_SIZ;\r
5769   openFileName.lpstrFileTitle    = fileTitle;\r
5770   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5771   openFileName.lpstrInitialDir   = NULL;\r
5772   openFileName.lpstrTitle        = dlgTitle;\r
5773   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5774     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5775     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5776     | (oldDialog ? 0 : OFN_EXPLORER);\r
5777   openFileName.nFileOffset       = 0;\r
5778   openFileName.nFileExtension    = 0;\r
5779   openFileName.lpstrDefExt       = defExt;\r
5780   openFileName.lCustData         = (LONG) number;\r
5781   openFileName.lpfnHook          = oldDialog ?\r
5782     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5783   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5784 \r
5785   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5786                         GetOpenFileName(&openFileName)) {\r
5787     /* open the file */\r
5788     f = fopen(openFileName.lpstrFile, write);\r
5789     if (f == NULL) {\r
5790       MessageBox(hwnd, _("File open failed"), NULL,\r
5791                  MB_OK|MB_ICONEXCLAMATION);\r
5792       return NULL;\r
5793     }\r
5794   } else {\r
5795     int err = CommDlgExtendedError();\r
5796     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5797     return FALSE;\r
5798   }\r
5799   return f;\r
5800 }\r
5801 \r
5802 \r
5803 \r
5804 VOID APIENTRY\r
5805 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5806 {\r
5807   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5808 \r
5809   /*\r
5810    * Get the first pop-up menu in the menu template. This is the\r
5811    * menu that TrackPopupMenu displays.\r
5812    */\r
5813   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5814   TranslateOneMenu(10, hmenuTrackPopup);\r
5815 \r
5816   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5817 \r
5818   /*\r
5819    * TrackPopup uses screen coordinates, so convert the\r
5820    * coordinates of the mouse click to screen coordinates.\r
5821    */\r
5822   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5823 \r
5824   /* Draw and track the floating pop-up menu. */\r
5825   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5826                  pt.x, pt.y, 0, hwnd, NULL);\r
5827 \r
5828   /* Destroy the menu.*/\r
5829   DestroyMenu(hmenu);\r
5830 }\r
5831    \r
5832 typedef struct {\r
5833   HWND hDlg, hText;\r
5834   int sizeX, sizeY, newSizeX, newSizeY;\r
5835   HDWP hdwp;\r
5836 } ResizeEditPlusButtonsClosure;\r
5837 \r
5838 BOOL CALLBACK\r
5839 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5840 {\r
5841   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5842   RECT rect;\r
5843   POINT pt;\r
5844 \r
5845   if (hChild == cl->hText) return TRUE;\r
5846   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5847   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5848   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5849   ScreenToClient(cl->hDlg, &pt);\r
5850   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5851     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5852   return TRUE;\r
5853 }\r
5854 \r
5855 /* Resize a dialog that has a (rich) edit field filling most of\r
5856    the top, with a row of buttons below */\r
5857 VOID\r
5858 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5859 {\r
5860   RECT rectText;\r
5861   int newTextHeight, newTextWidth;\r
5862   ResizeEditPlusButtonsClosure cl;\r
5863   \r
5864   /*if (IsIconic(hDlg)) return;*/\r
5865   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5866   \r
5867   cl.hdwp = BeginDeferWindowPos(8);\r
5868 \r
5869   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5870   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5871   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5872   if (newTextHeight < 0) {\r
5873     newSizeY += -newTextHeight;\r
5874     newTextHeight = 0;\r
5875   }\r
5876   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5877     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5878 \r
5879   cl.hDlg = hDlg;\r
5880   cl.hText = hText;\r
5881   cl.sizeX = sizeX;\r
5882   cl.sizeY = sizeY;\r
5883   cl.newSizeX = newSizeX;\r
5884   cl.newSizeY = newSizeY;\r
5885   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5886 \r
5887   EndDeferWindowPos(cl.hdwp);\r
5888 }\r
5889 \r
5890 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5891 {\r
5892     RECT    rChild, rParent;\r
5893     int     wChild, hChild, wParent, hParent;\r
5894     int     wScreen, hScreen, xNew, yNew;\r
5895     HDC     hdc;\r
5896 \r
5897     /* Get the Height and Width of the child window */\r
5898     GetWindowRect (hwndChild, &rChild);\r
5899     wChild = rChild.right - rChild.left;\r
5900     hChild = rChild.bottom - rChild.top;\r
5901 \r
5902     /* Get the Height and Width of the parent window */\r
5903     GetWindowRect (hwndParent, &rParent);\r
5904     wParent = rParent.right - rParent.left;\r
5905     hParent = rParent.bottom - rParent.top;\r
5906 \r
5907     /* Get the display limits */\r
5908     hdc = GetDC (hwndChild);\r
5909     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5910     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5911     ReleaseDC(hwndChild, hdc);\r
5912 \r
5913     /* Calculate new X position, then adjust for screen */\r
5914     xNew = rParent.left + ((wParent - wChild) /2);\r
5915     if (xNew < 0) {\r
5916         xNew = 0;\r
5917     } else if ((xNew+wChild) > wScreen) {\r
5918         xNew = wScreen - wChild;\r
5919     }\r
5920 \r
5921     /* Calculate new Y position, then adjust for screen */\r
5922     if( mode == 0 ) {\r
5923         yNew = rParent.top  + ((hParent - hChild) /2);\r
5924     }\r
5925     else {\r
5926         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5927     }\r
5928 \r
5929     if (yNew < 0) {\r
5930         yNew = 0;\r
5931     } else if ((yNew+hChild) > hScreen) {\r
5932         yNew = hScreen - hChild;\r
5933     }\r
5934 \r
5935     /* Set it, and return */\r
5936     return SetWindowPos (hwndChild, NULL,\r
5937                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5938 }\r
5939 \r
5940 /* Center one window over another */\r
5941 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5942 {\r
5943     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5944 }\r
5945 \r
5946 /*---------------------------------------------------------------------------*\\r
5947  *\r
5948  * Startup Dialog functions\r
5949  *\r
5950 \*---------------------------------------------------------------------------*/\r
5951 void\r
5952 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5953 {\r
5954   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5955 \r
5956   while (*cd != NULL) {\r
5957     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5958     cd++;\r
5959   }\r
5960 }\r
5961 \r
5962 void\r
5963 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5964 {\r
5965   char buf1[MAX_ARG_LEN];\r
5966   int len;\r
5967 \r
5968   if (str[0] == '@') {\r
5969     FILE* f = fopen(str + 1, "r");\r
5970     if (f == NULL) {\r
5971       DisplayFatalError(str + 1, errno, 2);\r
5972       return;\r
5973     }\r
5974     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5975     fclose(f);\r
5976     buf1[len] = NULLCHAR;\r
5977     str = buf1;\r
5978   }\r
5979 \r
5980   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5981 \r
5982   for (;;) {\r
5983     char buf[MSG_SIZ];\r
5984     char *end = strchr(str, '\n');\r
5985     if (end == NULL) return;\r
5986     memcpy(buf, str, end - str);\r
5987     buf[end - str] = NULLCHAR;\r
5988     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5989     str = end + 1;\r
5990   }\r
5991 }\r
5992 \r
5993 void\r
5994 SetStartupDialogEnables(HWND hDlg)\r
5995 {\r
5996   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5997     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5998     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5999   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6000     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6001   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6002     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6003   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6004     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6005   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6006     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6007     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6008     IsDlgButtonChecked(hDlg, OPT_View));\r
6009 }\r
6010 \r
6011 char *\r
6012 QuoteForFilename(char *filename)\r
6013 {\r
6014   int dquote, space;\r
6015   dquote = strchr(filename, '"') != NULL;\r
6016   space = strchr(filename, ' ') != NULL;\r
6017   if (dquote || space) {\r
6018     if (dquote) {\r
6019       return "'";\r
6020     } else {\r
6021       return "\"";\r
6022     }\r
6023   } else {\r
6024     return "";\r
6025   }\r
6026 }\r
6027 \r
6028 VOID\r
6029 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6030 {\r
6031   char buf[MSG_SIZ];\r
6032   char *q;\r
6033 \r
6034   InitComboStringsFromOption(hwndCombo, nthnames);\r
6035   q = QuoteForFilename(nthcp);\r
6036     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6037   if (*nthdir != NULLCHAR) {\r
6038     q = QuoteForFilename(nthdir);\r
6039       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6040   }\r
6041   if (*nthcp == NULLCHAR) {\r
6042     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6043   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6044     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6045     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6046   }\r
6047 }\r
6048 \r
6049 LRESULT CALLBACK\r
6050 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6051 {\r
6052   char buf[MSG_SIZ];\r
6053   HANDLE hwndCombo;\r
6054   char *p;\r
6055 \r
6056   switch (message) {\r
6057   case WM_INITDIALOG:\r
6058     /* Center the dialog */\r
6059     CenterWindow (hDlg, GetDesktopWindow());\r
6060     Translate(hDlg, DLG_Startup);\r
6061     /* Initialize the dialog items */\r
6062     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6063                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6064                   firstChessProgramNames);\r
6065     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6066                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6067                   secondChessProgramNames);\r
6068     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6069     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6070       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6071     if (*appData.icsHelper != NULLCHAR) {\r
6072       char *q = QuoteForFilename(appData.icsHelper);\r
6073       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6074     }\r
6075     if (*appData.icsHost == NULLCHAR) {\r
6076       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6077       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6078     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6079       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6080       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6081     }\r
6082 \r
6083     if (appData.icsActive) {\r
6084       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6085     }\r
6086     else if (appData.noChessProgram) {\r
6087       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6088     }\r
6089     else {\r
6090       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6091     }\r
6092 \r
6093     SetStartupDialogEnables(hDlg);\r
6094     return TRUE;\r
6095 \r
6096   case WM_COMMAND:\r
6097     switch (LOWORD(wParam)) {\r
6098     case IDOK:\r
6099       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6100         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6101         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6102         p = buf;\r
6103         ParseArgs(StringGet, &p);\r
6104         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6105         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6106         p = buf;\r
6107         ParseArgs(StringGet, &p);\r
6108         appData.noChessProgram = FALSE;\r
6109         appData.icsActive = FALSE;\r
6110       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6111         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6112         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6113         p = buf;\r
6114         ParseArgs(StringGet, &p);\r
6115         if (appData.zippyPlay) {\r
6116           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6117           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6118           p = buf;\r
6119           ParseArgs(StringGet, &p);\r
6120         }\r
6121       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6122         appData.noChessProgram = TRUE;\r
6123         appData.icsActive = FALSE;\r
6124       } else {\r
6125         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6126                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6127         return TRUE;\r
6128       }\r
6129       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6130         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6131         p = buf;\r
6132         ParseArgs(StringGet, &p);\r
6133       }\r
6134       EndDialog(hDlg, TRUE);\r
6135       return TRUE;\r
6136 \r
6137     case IDCANCEL:\r
6138       ExitEvent(0);\r
6139       return TRUE;\r
6140 \r
6141     case IDM_HELPCONTENTS:\r
6142       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6143         MessageBox (GetFocus(),\r
6144                     _("Unable to activate help"),\r
6145                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6146       }\r
6147       break;\r
6148 \r
6149     default:\r
6150       SetStartupDialogEnables(hDlg);\r
6151       break;\r
6152     }\r
6153     break;\r
6154   }\r
6155   return FALSE;\r
6156 }\r
6157 \r
6158 /*---------------------------------------------------------------------------*\\r
6159  *\r
6160  * About box dialog functions\r
6161  *\r
6162 \*---------------------------------------------------------------------------*/\r
6163 \r
6164 /* Process messages for "About" dialog box */\r
6165 LRESULT CALLBACK\r
6166 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6167 {\r
6168   switch (message) {\r
6169   case WM_INITDIALOG: /* message: initialize dialog box */\r
6170     /* Center the dialog over the application window */\r
6171     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6172     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6173     Translate(hDlg, ABOUTBOX);\r
6174     JAWS_COPYRIGHT\r
6175     return (TRUE);\r
6176 \r
6177   case WM_COMMAND: /* message: received a command */\r
6178     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6179         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6180       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6181       return (TRUE);\r
6182     }\r
6183     break;\r
6184   }\r
6185   return (FALSE);\r
6186 }\r
6187 \r
6188 /*---------------------------------------------------------------------------*\\r
6189  *\r
6190  * Comment Dialog functions\r
6191  *\r
6192 \*---------------------------------------------------------------------------*/\r
6193 \r
6194 LRESULT CALLBACK\r
6195 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6196 {\r
6197   static HANDLE hwndText = NULL;\r
6198   int len, newSizeX, newSizeY, flags;\r
6199   static int sizeX, sizeY;\r
6200   char *str;\r
6201   RECT rect;\r
6202   MINMAXINFO *mmi;\r
6203 \r
6204   switch (message) {\r
6205   case WM_INITDIALOG: /* message: initialize dialog box */\r
6206     /* Initialize the dialog items */\r
6207     Translate(hDlg, DLG_EditComment);\r
6208     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6209     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6210     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6211     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6212     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6213     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6214     SetWindowText(hDlg, commentTitle);\r
6215     if (editComment) {\r
6216       SetFocus(hwndText);\r
6217     } else {\r
6218       SetFocus(GetDlgItem(hDlg, IDOK));\r
6219     }\r
6220     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6221                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6222                 MAKELPARAM(FALSE, 0));\r
6223     /* Size and position the dialog */\r
6224     if (!commentDialog) {\r
6225       commentDialog = hDlg;\r
6226       flags = SWP_NOZORDER;\r
6227       GetClientRect(hDlg, &rect);\r
6228       sizeX = rect.right;\r
6229       sizeY = rect.bottom;\r
6230       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6231           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6232         WINDOWPLACEMENT wp;\r
6233         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6234         wp.length = sizeof(WINDOWPLACEMENT);\r
6235         wp.flags = 0;\r
6236         wp.showCmd = SW_SHOW;\r
6237         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6238         wp.rcNormalPosition.left = wpComment.x;\r
6239         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6240         wp.rcNormalPosition.top = wpComment.y;\r
6241         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6242         SetWindowPlacement(hDlg, &wp);\r
6243 \r
6244         GetClientRect(hDlg, &rect);\r
6245         newSizeX = rect.right;\r
6246         newSizeY = rect.bottom;\r
6247         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6248                               newSizeX, newSizeY);\r
6249         sizeX = newSizeX;\r
6250         sizeY = newSizeY;\r
6251       }\r
6252     }\r
6253     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6254     return FALSE;\r
6255 \r
6256   case WM_COMMAND: /* message: received a command */\r
6257     switch (LOWORD(wParam)) {\r
6258     case IDOK:\r
6259       if (editComment) {\r
6260         char *p, *q;\r
6261         /* Read changed options from the dialog box */\r
6262         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6263         len = GetWindowTextLength(hwndText);\r
6264         str = (char *) malloc(len + 1);\r
6265         GetWindowText(hwndText, str, len + 1);\r
6266         p = q = str;\r
6267         while (*q) {\r
6268           if (*q == '\r')\r
6269             q++;\r
6270           else\r
6271             *p++ = *q++;\r
6272         }\r
6273         *p = NULLCHAR;\r
6274         ReplaceComment(commentIndex, str);\r
6275         free(str);\r
6276       }\r
6277       CommentPopDown();\r
6278       return TRUE;\r
6279 \r
6280     case IDCANCEL:\r
6281     case OPT_CancelComment:\r
6282       CommentPopDown();\r
6283       return TRUE;\r
6284 \r
6285     case OPT_ClearComment:\r
6286       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6287       break;\r
6288 \r
6289     case OPT_EditComment:\r
6290       EditCommentEvent();\r
6291       return TRUE;\r
6292 \r
6293     default:\r
6294       break;\r
6295     }\r
6296     break;\r
6297 \r
6298   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6299         if( wParam == OPT_CommentText ) {\r
6300             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6301 \r
6302             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6303                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6304                 POINTL pt;\r
6305                 LRESULT index;\r
6306 \r
6307                 pt.x = LOWORD( lpMF->lParam );\r
6308                 pt.y = HIWORD( lpMF->lParam );\r
6309 \r
6310                 if(lpMF->msg == WM_CHAR) {\r
6311                         CHARRANGE sel;\r
6312                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6313                         index = sel.cpMin;\r
6314                 } else\r
6315                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6316 \r
6317                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6318                 len = GetWindowTextLength(hwndText);\r
6319                 str = (char *) malloc(len + 1);\r
6320                 GetWindowText(hwndText, str, len + 1);\r
6321                 ReplaceComment(commentIndex, str);\r
6322                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6323                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6324                 free(str);\r
6325 \r
6326                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6327                 lpMF->msg = WM_USER;\r
6328 \r
6329                 return TRUE;\r
6330             }\r
6331         }\r
6332         break;\r
6333 \r
6334   case WM_SIZE:\r
6335     newSizeX = LOWORD(lParam);\r
6336     newSizeY = HIWORD(lParam);\r
6337     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6338     sizeX = newSizeX;\r
6339     sizeY = newSizeY;\r
6340     break;\r
6341 \r
6342   case WM_GETMINMAXINFO:\r
6343     /* Prevent resizing window too small */\r
6344     mmi = (MINMAXINFO *) lParam;\r
6345     mmi->ptMinTrackSize.x = 100;\r
6346     mmi->ptMinTrackSize.y = 100;\r
6347     break;\r
6348   }\r
6349   return FALSE;\r
6350 }\r
6351 \r
6352 VOID\r
6353 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6354 {\r
6355   FARPROC lpProc;\r
6356   char *p, *q;\r
6357 \r
6358   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6359 \r
6360   if (str == NULL) str = "";\r
6361   p = (char *) malloc(2 * strlen(str) + 2);\r
6362   q = p;\r
6363   while (*str) {\r
6364     if (*str == '\n') *q++ = '\r';\r
6365     *q++ = *str++;\r
6366   }\r
6367   *q = NULLCHAR;\r
6368   if (commentText != NULL) free(commentText);\r
6369 \r
6370   commentIndex = index;\r
6371   commentTitle = title;\r
6372   commentText = p;\r
6373   editComment = edit;\r
6374 \r
6375   if (commentDialog) {\r
6376     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6377     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6378   } else {\r
6379     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6380     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6381                  hwndMain, (DLGPROC)lpProc);\r
6382     FreeProcInstance(lpProc);\r
6383   }\r
6384   commentUp = TRUE;\r
6385 }\r
6386 \r
6387 \r
6388 /*---------------------------------------------------------------------------*\\r
6389  *\r
6390  * Type-in move dialog functions\r
6391  * \r
6392 \*---------------------------------------------------------------------------*/\r
6393 \r
6394 LRESULT CALLBACK\r
6395 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6396 {\r
6397   char move[MSG_SIZ];\r
6398   HWND hInput;\r
6399   ChessMove moveType;\r
6400   int fromX, fromY, toX, toY;\r
6401   char promoChar;\r
6402 \r
6403   switch (message) {\r
6404   case WM_INITDIALOG:\r
6405     move[0] = (char) lParam;\r
6406     move[1] = NULLCHAR;\r
6407     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6408     Translate(hDlg, DLG_TypeInMove);\r
6409     hInput = GetDlgItem(hDlg, OPT_Move);\r
6410     SetWindowText(hInput, move);\r
6411     SetFocus(hInput);\r
6412     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6413     return FALSE;\r
6414 \r
6415   case WM_COMMAND:\r
6416     switch (LOWORD(wParam)) {\r
6417     case IDOK:
6418 \r
6419       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6420       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6421       { int n; Board board;\r
6422         // [HGM] FENedit\r
6423         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6424                 EditPositionPasteFEN(move);\r
6425                 EndDialog(hDlg, TRUE);\r
6426                 return TRUE;\r
6427         }\r
6428         // [HGM] movenum: allow move number to be typed in any mode\r
6429         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6430           ToNrEvent(2*n-1);\r
6431           EndDialog(hDlg, TRUE);\r
6432           return TRUE;\r
6433         }\r
6434       }\r
6435       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6436         gameMode != Training) {\r
6437         DisplayMoveError(_("Displayed move is not current"));\r
6438       } else {\r
6439 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6440         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6441           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6442         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6443         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6444           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6445           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6446         } else {\r
6447           DisplayMoveError(_("Could not parse move"));\r
6448         }\r
6449       }\r
6450       EndDialog(hDlg, TRUE);\r
6451       return TRUE;\r
6452     case IDCANCEL:\r
6453       EndDialog(hDlg, FALSE);\r
6454       return TRUE;\r
6455     default:\r
6456       break;\r
6457     }\r
6458     break;\r
6459   }\r
6460   return FALSE;\r
6461 }\r
6462 \r
6463 VOID\r
6464 PopUpMoveDialog(char firstchar)\r
6465 {\r
6466     FARPROC lpProc;\r
6467     \r
6468     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6469         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6470         gameMode == AnalyzeMode || gameMode == EditGame || \r
6471         gameMode == EditPosition || gameMode == IcsExamining ||\r
6472         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6473         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6474                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6475                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6476         gameMode == Training) {\r
6477       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6478       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6479         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6480       FreeProcInstance(lpProc);\r
6481     }\r
6482 }\r
6483 \r
6484 /*---------------------------------------------------------------------------*\\r
6485  *\r
6486  * Type-in name dialog functions\r
6487  * \r
6488 \*---------------------------------------------------------------------------*/\r
6489 \r
6490 LRESULT CALLBACK\r
6491 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6492 {\r
6493   char move[MSG_SIZ];\r
6494   HWND hInput;\r
6495 \r
6496   switch (message) {\r
6497   case WM_INITDIALOG:\r
6498     move[0] = (char) lParam;\r
6499     move[1] = NULLCHAR;\r
6500     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6501     Translate(hDlg, DLG_TypeInName);\r
6502     hInput = GetDlgItem(hDlg, OPT_Name);\r
6503     SetWindowText(hInput, move);\r
6504     SetFocus(hInput);\r
6505     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6506     return FALSE;\r
6507 \r
6508   case WM_COMMAND:\r
6509     switch (LOWORD(wParam)) {\r
6510     case IDOK:\r
6511       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6512       appData.userName = strdup(move);\r
6513       SetUserLogo();\r
6514       SetGameInfo();\r
6515       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6516         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6517         DisplayTitle(move);\r
6518       }\r
6519 \r
6520 \r
6521       EndDialog(hDlg, TRUE);\r
6522       return TRUE;\r
6523     case IDCANCEL:\r
6524       EndDialog(hDlg, FALSE);\r
6525       return TRUE;\r
6526     default:\r
6527       break;\r
6528     }\r
6529     break;\r
6530   }\r
6531   return FALSE;\r
6532 }\r
6533 \r
6534 VOID\r
6535 PopUpNameDialog(char firstchar)\r
6536 {\r
6537     FARPROC lpProc;\r
6538     \r
6539       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6540       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6541         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6542       FreeProcInstance(lpProc);\r
6543 }\r
6544 \r
6545 /*---------------------------------------------------------------------------*\\r
6546  *\r
6547  *  Error dialogs\r
6548  * \r
6549 \*---------------------------------------------------------------------------*/\r
6550 \r
6551 /* Nonmodal error box */\r
6552 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6553                              WPARAM wParam, LPARAM lParam);\r
6554 \r
6555 VOID\r
6556 ErrorPopUp(char *title, char *content)\r
6557 {\r
6558   FARPROC lpProc;\r
6559   char *p, *q;\r
6560   BOOLEAN modal = hwndMain == NULL;\r
6561 \r
6562   p = content;\r
6563   q = errorMessage;\r
6564   while (*p) {\r
6565     if (*p == '\n') {\r
6566       if (modal) {\r
6567         *q++ = ' ';\r
6568         p++;\r
6569       } else {\r
6570         *q++ = '\r';\r
6571         *q++ = *p++;\r
6572       }\r
6573     } else {\r
6574       *q++ = *p++;\r
6575     }\r
6576   }\r
6577   *q = NULLCHAR;\r
6578   strncpy(errorTitle, title, sizeof(errorTitle));\r
6579   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6580   \r
6581   if (modal) {\r
6582     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6583   } else {\r
6584     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6585     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6586                  hwndMain, (DLGPROC)lpProc);\r
6587     FreeProcInstance(lpProc);\r
6588   }\r
6589 }\r
6590 \r
6591 VOID\r
6592 ErrorPopDown()\r
6593 {\r
6594   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6595   if (errorDialog == NULL) return;\r
6596   DestroyWindow(errorDialog);\r
6597   errorDialog = NULL;\r
6598   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6599 }\r
6600 \r
6601 LRESULT CALLBACK\r
6602 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6603 {\r
6604   HANDLE hwndText;\r
6605   RECT rChild;\r
6606 \r
6607   switch (message) {\r
6608   case WM_INITDIALOG:\r
6609     GetWindowRect(hDlg, &rChild);\r
6610 \r
6611     /*\r
6612     SetWindowPos(hDlg, NULL, rChild.left,\r
6613       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6614       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6615     */\r
6616 \r
6617     /* \r
6618         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6619         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6620         and it doesn't work when you resize the dialog.\r
6621         For now, just give it a default position.\r
6622     */\r
6623     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6624     Translate(hDlg, DLG_Error);\r
6625 \r
6626     errorDialog = hDlg;\r
6627     SetWindowText(hDlg, errorTitle);\r
6628     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6629     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6630     return FALSE;\r
6631 \r
6632   case WM_COMMAND:\r
6633     switch (LOWORD(wParam)) {\r
6634     case IDOK:\r
6635     case IDCANCEL:\r
6636       if (errorDialog == hDlg) errorDialog = NULL;\r
6637       DestroyWindow(hDlg);\r
6638       return TRUE;\r
6639 \r
6640     default:\r
6641       break;\r
6642     }\r
6643     break;\r
6644   }\r
6645   return FALSE;\r
6646 }\r
6647 \r
6648 #ifdef GOTHIC\r
6649 HWND gothicDialog = NULL;\r
6650 \r
6651 LRESULT CALLBACK\r
6652 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6653 {\r
6654   HANDLE hwndText;\r
6655   RECT rChild;\r
6656   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6657 \r
6658   switch (message) {\r
6659   case WM_INITDIALOG:\r
6660     GetWindowRect(hDlg, &rChild);\r
6661 \r
6662     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6663                                                              SWP_NOZORDER);\r
6664 \r
6665     /* \r
6666         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6667         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6668         and it doesn't work when you resize the dialog.\r
6669         For now, just give it a default position.\r
6670     */\r
6671     gothicDialog = hDlg;\r
6672     SetWindowText(hDlg, errorTitle);\r
6673     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6674     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6675     return FALSE;\r
6676 \r
6677   case WM_COMMAND:\r
6678     switch (LOWORD(wParam)) {\r
6679     case IDOK:\r
6680     case IDCANCEL:\r
6681       if (errorDialog == hDlg) errorDialog = NULL;\r
6682       DestroyWindow(hDlg);\r
6683       return TRUE;\r
6684 \r
6685     default:\r
6686       break;\r
6687     }\r
6688     break;\r
6689   }\r
6690   return FALSE;\r
6691 }\r
6692 \r
6693 VOID\r
6694 GothicPopUp(char *title, VariantClass variant)\r
6695 {\r
6696   FARPROC lpProc;\r
6697   static char *lastTitle;\r
6698 \r
6699   strncpy(errorTitle, title, sizeof(errorTitle));\r
6700   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6701 \r
6702   if(lastTitle != title && gothicDialog != NULL) {\r
6703     DestroyWindow(gothicDialog);\r
6704     gothicDialog = NULL;\r
6705   }\r
6706   if(variant != VariantNormal && gothicDialog == NULL) {\r
6707     title = lastTitle;\r
6708     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6709     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6710                  hwndMain, (DLGPROC)lpProc);\r
6711     FreeProcInstance(lpProc);\r
6712   }\r
6713 }\r
6714 #endif\r
6715 \r
6716 /*---------------------------------------------------------------------------*\\r
6717  *\r
6718  *  Ics Interaction console functions\r
6719  *\r
6720 \*---------------------------------------------------------------------------*/\r
6721 \r
6722 #define HISTORY_SIZE 64\r
6723 static char *history[HISTORY_SIZE];\r
6724 int histIn = 0, histP = 0;\r
6725 \r
6726 VOID\r
6727 SaveInHistory(char *cmd)\r
6728 {\r
6729   if (history[histIn] != NULL) {\r
6730     free(history[histIn]);\r
6731     history[histIn] = NULL;\r
6732   }\r
6733   if (*cmd == NULLCHAR) return;\r
6734   history[histIn] = StrSave(cmd);\r
6735   histIn = (histIn + 1) % HISTORY_SIZE;\r
6736   if (history[histIn] != NULL) {\r
6737     free(history[histIn]);\r
6738     history[histIn] = NULL;\r
6739   }\r
6740   histP = histIn;\r
6741 }\r
6742 \r
6743 char *\r
6744 PrevInHistory(char *cmd)\r
6745 {\r
6746   int newhp;\r
6747   if (histP == histIn) {\r
6748     if (history[histIn] != NULL) free(history[histIn]);\r
6749     history[histIn] = StrSave(cmd);\r
6750   }\r
6751   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6752   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6753   histP = newhp;\r
6754   return history[histP];\r
6755 }\r
6756 \r
6757 char *\r
6758 NextInHistory()\r
6759 {\r
6760   if (histP == histIn) return NULL;\r
6761   histP = (histP + 1) % HISTORY_SIZE;\r
6762   return history[histP];   \r
6763 }\r
6764 \r
6765 HMENU\r
6766 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6767 {\r
6768   HMENU hmenu, h;\r
6769   int i = 0;\r
6770   hmenu = LoadMenu(hInst, "TextMenu");\r
6771   h = GetSubMenu(hmenu, 0);\r
6772   while (e->item) {\r
6773     if (strcmp(e->item, "-") == 0) {\r
6774       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6775     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6776       int flags = MF_STRING, j = 0;\r
6777       if (e->item[0] == '|') {\r
6778         flags |= MF_MENUBARBREAK;\r
6779         j++;\r
6780       }\r
6781       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6782       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6783     }\r
6784     e++;\r
6785     i++;\r
6786   } \r
6787   return hmenu;\r
6788 }\r
6789 \r
6790 WNDPROC consoleTextWindowProc;\r
6791 \r
6792 void\r
6793 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6794 {\r
6795   char buf[MSG_SIZ], name[MSG_SIZ];\r
6796   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6797   CHARRANGE sel;\r
6798 \r
6799   if (!getname) {\r
6800     SetWindowText(hInput, command);\r
6801     if (immediate) {\r
6802       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6803     } else {\r
6804       sel.cpMin = 999999;\r
6805       sel.cpMax = 999999;\r
6806       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6807       SetFocus(hInput);\r
6808     }\r
6809     return;\r
6810   }    \r
6811   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6812   if (sel.cpMin == sel.cpMax) {\r
6813     /* Expand to surrounding word */\r
6814     TEXTRANGE tr;\r
6815     do {\r
6816       tr.chrg.cpMax = sel.cpMin;\r
6817       tr.chrg.cpMin = --sel.cpMin;\r
6818       if (sel.cpMin < 0) break;\r
6819       tr.lpstrText = name;\r
6820       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6821     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6822     sel.cpMin++;\r
6823 \r
6824     do {\r
6825       tr.chrg.cpMin = sel.cpMax;\r
6826       tr.chrg.cpMax = ++sel.cpMax;\r
6827       tr.lpstrText = name;\r
6828       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6829     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6830     sel.cpMax--;\r
6831 \r
6832     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6833       MessageBeep(MB_ICONEXCLAMATION);\r
6834       return;\r
6835     }\r
6836     tr.chrg = sel;\r
6837     tr.lpstrText = name;\r
6838     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6839   } else {\r
6840     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6841       MessageBeep(MB_ICONEXCLAMATION);\r
6842       return;\r
6843     }\r
6844     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6845   }\r
6846   if (immediate) {\r
6847     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6848     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6849     SetWindowText(hInput, buf);\r
6850     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6851   } else {\r
6852     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6853       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6854     SetWindowText(hInput, buf);\r
6855     sel.cpMin = 999999;\r
6856     sel.cpMax = 999999;\r
6857     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6858     SetFocus(hInput);\r
6859   }\r
6860 }\r
6861 \r
6862 LRESULT CALLBACK \r
6863 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6864 {\r
6865   HWND hInput;\r
6866   CHARRANGE sel;\r
6867 \r
6868   switch (message) {\r
6869   case WM_KEYDOWN:\r
6870     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6871     if(wParam=='R') return 0;\r
6872     switch (wParam) {\r
6873     case VK_PRIOR:\r
6874       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6875       return 0;\r
6876     case VK_NEXT:\r
6877       sel.cpMin = 999999;\r
6878       sel.cpMax = 999999;\r
6879       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6880       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6881       return 0;\r
6882     }\r
6883     break;\r
6884   case WM_CHAR:\r
6885    if(wParam != '\022') {\r
6886     if (wParam == '\t') {\r
6887       if (GetKeyState(VK_SHIFT) < 0) {\r
6888         /* shifted */\r
6889         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6890         if (buttonDesc[0].hwnd) {\r
6891           SetFocus(buttonDesc[0].hwnd);\r
6892         } else {\r
6893           SetFocus(hwndMain);\r
6894         }\r
6895       } else {\r
6896         /* unshifted */\r
6897         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6898       }\r
6899     } else {\r
6900       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6901       JAWS_DELETE( SetFocus(hInput); )\r
6902       SendMessage(hInput, message, wParam, lParam);\r
6903     }\r
6904     return 0;\r
6905    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6906    lParam = -1;\r
6907   case WM_RBUTTONDOWN:\r
6908     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6909       /* Move selection here if it was empty */\r
6910       POINT pt;\r
6911       pt.x = LOWORD(lParam);\r
6912       pt.y = HIWORD(lParam);\r
6913       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6914       if (sel.cpMin == sel.cpMax) {\r
6915         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6916         sel.cpMax = sel.cpMin;\r
6917         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6918       }\r
6919       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6920 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6921       POINT pt;\r
6922       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6923       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6924       if (sel.cpMin == sel.cpMax) {\r
6925         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6926         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6927       }\r
6928       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6929         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6930       }\r
6931       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6932       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6933       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6934       MenuPopup(hwnd, pt, hmenu, -1);\r
6935 }\r
6936     }\r
6937     return 0;\r
6938   case WM_RBUTTONUP:\r
6939     if (GetKeyState(VK_SHIFT) & ~1) {\r
6940       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6941         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6942     }\r
6943     return 0;\r
6944   case WM_PASTE:\r
6945     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6946     SetFocus(hInput);\r
6947     return SendMessage(hInput, message, wParam, lParam);\r
6948   case WM_MBUTTONDOWN:\r
6949     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6950   case WM_COMMAND:\r
6951     switch (LOWORD(wParam)) {\r
6952     case IDM_QuickPaste:\r
6953       {\r
6954         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6955         if (sel.cpMin == sel.cpMax) {\r
6956           MessageBeep(MB_ICONEXCLAMATION);\r
6957           return 0;\r
6958         }\r
6959         SendMessage(hwnd, WM_COPY, 0, 0);\r
6960         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6961         SendMessage(hInput, WM_PASTE, 0, 0);\r
6962         SetFocus(hInput);\r
6963         return 0;\r
6964       }\r
6965     case IDM_Cut:\r
6966       SendMessage(hwnd, WM_CUT, 0, 0);\r
6967       return 0;\r
6968     case IDM_Paste:\r
6969       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6970       return 0;\r
6971     case IDM_Copy:\r
6972       SendMessage(hwnd, WM_COPY, 0, 0);\r
6973       return 0;\r
6974     default:\r
6975       {\r
6976         int i = LOWORD(wParam) - IDM_CommandX;\r
6977         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6978             icsTextMenuEntry[i].command != NULL) {\r
6979           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6980                    icsTextMenuEntry[i].getname,\r
6981                    icsTextMenuEntry[i].immediate);\r
6982           return 0;\r
6983         }\r
6984       }\r
6985       break;\r
6986     }\r
6987     break;\r
6988   }\r
6989   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6990 }\r
6991 \r
6992 WNDPROC consoleInputWindowProc;\r
6993 \r
6994 LRESULT CALLBACK\r
6995 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6996 {\r
6997   char buf[MSG_SIZ];\r
6998   char *p;\r
6999   static BOOL sendNextChar = FALSE;\r
7000   static BOOL quoteNextChar = FALSE;\r
7001   InputSource *is = consoleInputSource;\r
7002   CHARFORMAT cf;\r
7003   CHARRANGE sel;\r
7004 \r
7005   switch (message) {\r
7006   case WM_CHAR:\r
7007     if (!appData.localLineEditing || sendNextChar) {\r
7008       is->buf[0] = (CHAR) wParam;\r
7009       is->count = 1;\r
7010       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7011       sendNextChar = FALSE;\r
7012       return 0;\r
7013     }\r
7014     if (quoteNextChar) {\r
7015       buf[0] = (char) wParam;\r
7016       buf[1] = NULLCHAR;\r
7017       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7018       quoteNextChar = FALSE;\r
7019       return 0;\r
7020     }\r
7021     switch (wParam) {\r
7022     case '\r':   /* Enter key */\r
7023       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7024       if (consoleEcho) SaveInHistory(is->buf);\r
7025       is->buf[is->count++] = '\n';\r
7026       is->buf[is->count] = NULLCHAR;\r
7027       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7028       if (consoleEcho) {\r
7029         ConsoleOutput(is->buf, is->count, TRUE);\r
7030       } else if (appData.localLineEditing) {\r
7031         ConsoleOutput("\n", 1, TRUE);\r
7032       }\r
7033       /* fall thru */\r
7034     case '\033': /* Escape key */\r
7035       SetWindowText(hwnd, "");\r
7036       cf.cbSize = sizeof(CHARFORMAT);\r
7037       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7038       if (consoleEcho) {\r
7039         cf.crTextColor = textAttribs[ColorNormal].color;\r
7040       } else {\r
7041         cf.crTextColor = COLOR_ECHOOFF;\r
7042       }\r
7043       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7044       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7045       return 0;\r
7046     case '\t':   /* Tab key */\r
7047       if (GetKeyState(VK_SHIFT) < 0) {\r
7048         /* shifted */\r
7049         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7050       } else {\r
7051         /* unshifted */\r
7052         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7053         if (buttonDesc[0].hwnd) {\r
7054           SetFocus(buttonDesc[0].hwnd);\r
7055         } else {\r
7056           SetFocus(hwndMain);\r
7057         }\r
7058       }\r
7059       return 0;\r
7060     case '\023': /* Ctrl+S */\r
7061       sendNextChar = TRUE;\r
7062       return 0;\r
7063     case '\021': /* Ctrl+Q */\r
7064       quoteNextChar = TRUE;\r
7065       return 0;\r
7066     JAWS_REPLAY\r
7067     default:\r
7068       break;\r
7069     }\r
7070     break;\r
7071   case WM_KEYDOWN:\r
7072     switch (wParam) {\r
7073     case VK_UP:\r
7074       GetWindowText(hwnd, buf, MSG_SIZ);\r
7075       p = PrevInHistory(buf);\r
7076       if (p != NULL) {\r
7077         SetWindowText(hwnd, p);\r
7078         sel.cpMin = 999999;\r
7079         sel.cpMax = 999999;\r
7080         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7081         return 0;\r
7082       }\r
7083       break;\r
7084     case VK_DOWN:\r
7085       p = NextInHistory();\r
7086       if (p != NULL) {\r
7087         SetWindowText(hwnd, p);\r
7088         sel.cpMin = 999999;\r
7089         sel.cpMax = 999999;\r
7090         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7091         return 0;\r
7092       }\r
7093       break;\r
7094     case VK_HOME:\r
7095     case VK_END:\r
7096       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7097       /* fall thru */\r
7098     case VK_PRIOR:\r
7099     case VK_NEXT:\r
7100       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7101       return 0;\r
7102     }\r
7103     break;\r
7104   case WM_MBUTTONDOWN:\r
7105     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7106       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7107     break;\r
7108   case WM_RBUTTONUP:\r
7109     if (GetKeyState(VK_SHIFT) & ~1) {\r
7110       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7111         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7112     } else {\r
7113       POINT pt;\r
7114       HMENU hmenu;\r
7115       hmenu = LoadMenu(hInst, "InputMenu");\r
7116       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7117       if (sel.cpMin == sel.cpMax) {\r
7118         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7119         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7120       }\r
7121       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7122         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7123       }\r
7124       pt.x = LOWORD(lParam);\r
7125       pt.y = HIWORD(lParam);\r
7126       MenuPopup(hwnd, pt, hmenu, -1);\r
7127     }\r
7128     return 0;\r
7129   case WM_COMMAND:\r
7130     switch (LOWORD(wParam)) { \r
7131     case IDM_Undo:\r
7132       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7133       return 0;\r
7134     case IDM_SelectAll:\r
7135       sel.cpMin = 0;\r
7136       sel.cpMax = -1; /*999999?*/\r
7137       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7138       return 0;\r
7139     case IDM_Cut:\r
7140       SendMessage(hwnd, WM_CUT, 0, 0);\r
7141       return 0;\r
7142     case IDM_Paste:\r
7143       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7144       return 0;\r
7145     case IDM_Copy:\r
7146       SendMessage(hwnd, WM_COPY, 0, 0);\r
7147       return 0;\r
7148     }\r
7149     break;\r
7150   }\r
7151   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7152 }\r
7153 \r
7154 #define CO_MAX  100000\r
7155 #define CO_TRIM   1000\r
7156 \r
7157 LRESULT CALLBACK\r
7158 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7159 {\r
7160   static SnapData sd;\r
7161   HWND hText, hInput;\r
7162   RECT rect;\r
7163   static int sizeX, sizeY;\r
7164   int newSizeX, newSizeY;\r
7165   MINMAXINFO *mmi;\r
7166   WORD wMask;\r
7167 \r
7168   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7169   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7170 \r
7171   switch (message) {\r
7172   case WM_NOTIFY:\r
7173     if (((NMHDR*)lParam)->code == EN_LINK)\r
7174     {\r
7175       ENLINK *pLink = (ENLINK*)lParam;\r
7176       if (pLink->msg == WM_LBUTTONUP)\r
7177       {\r
7178         TEXTRANGE tr;\r
7179 \r
7180         tr.chrg = pLink->chrg;\r
7181         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7182         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7183         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7184         free(tr.lpstrText);\r
7185       }\r
7186     }\r
7187     break;\r
7188   case WM_INITDIALOG: /* message: initialize dialog box */\r
7189     hwndConsole = hDlg;\r
7190     SetFocus(hInput);\r
7191     consoleTextWindowProc = (WNDPROC)\r
7192       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7193     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7194     consoleInputWindowProc = (WNDPROC)\r
7195       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7196     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7197     Colorize(ColorNormal, TRUE);\r
7198     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7199     ChangedConsoleFont();\r
7200     GetClientRect(hDlg, &rect);\r
7201     sizeX = rect.right;\r
7202     sizeY = rect.bottom;\r
7203     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7204         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7205       WINDOWPLACEMENT wp;\r
7206       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7207       wp.length = sizeof(WINDOWPLACEMENT);\r
7208       wp.flags = 0;\r
7209       wp.showCmd = SW_SHOW;\r
7210       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7211       wp.rcNormalPosition.left = wpConsole.x;\r
7212       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7213       wp.rcNormalPosition.top = wpConsole.y;\r
7214       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7215       SetWindowPlacement(hDlg, &wp);\r
7216     }\r
7217 \r
7218    // [HGM] Chessknight's change 2004-07-13\r
7219    else { /* Determine Defaults */\r
7220        WINDOWPLACEMENT wp;\r
7221        wpConsole.x = wpMain.width + 1;\r
7222        wpConsole.y = wpMain.y;\r
7223        wpConsole.width = screenWidth -  wpMain.width;\r
7224        wpConsole.height = wpMain.height;\r
7225        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7226        wp.length = sizeof(WINDOWPLACEMENT);\r
7227        wp.flags = 0;\r
7228        wp.showCmd = SW_SHOW;\r
7229        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7230        wp.rcNormalPosition.left = wpConsole.x;\r
7231        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7232        wp.rcNormalPosition.top = wpConsole.y;\r
7233        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7234        SetWindowPlacement(hDlg, &wp);\r
7235     }\r
7236 \r
7237    // Allow hText to highlight URLs and send notifications on them\r
7238    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7239    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7240    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7241    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7242 \r
7243     return FALSE;\r
7244 \r
7245   case WM_SETFOCUS:\r
7246     SetFocus(hInput);\r
7247     return 0;\r
7248 \r
7249   case WM_CLOSE:\r
7250     ExitEvent(0);\r
7251     /* not reached */\r
7252     break;\r
7253 \r
7254   case WM_SIZE:\r
7255     if (IsIconic(hDlg)) break;\r
7256     newSizeX = LOWORD(lParam);\r
7257     newSizeY = HIWORD(lParam);\r
7258     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7259       RECT rectText, rectInput;\r
7260       POINT pt;\r
7261       int newTextHeight, newTextWidth;\r
7262       GetWindowRect(hText, &rectText);\r
7263       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7264       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7265       if (newTextHeight < 0) {\r
7266         newSizeY += -newTextHeight;\r
7267         newTextHeight = 0;\r
7268       }\r
7269       SetWindowPos(hText, NULL, 0, 0,\r
7270         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7271       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7272       pt.x = rectInput.left;\r
7273       pt.y = rectInput.top + newSizeY - sizeY;\r
7274       ScreenToClient(hDlg, &pt);\r
7275       SetWindowPos(hInput, NULL, \r
7276         pt.x, pt.y, /* needs client coords */   \r
7277         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7278         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7279     }\r
7280     sizeX = newSizeX;\r
7281     sizeY = newSizeY;\r
7282     break;\r
7283 \r
7284   case WM_GETMINMAXINFO:\r
7285     /* Prevent resizing window too small */\r
7286     mmi = (MINMAXINFO *) lParam;\r
7287     mmi->ptMinTrackSize.x = 100;\r
7288     mmi->ptMinTrackSize.y = 100;\r
7289     break;\r
7290 \r
7291   /* [AS] Snapping */\r
7292   case WM_ENTERSIZEMOVE:\r
7293     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7294 \r
7295   case WM_SIZING:\r
7296     return OnSizing( &sd, hDlg, wParam, lParam );\r
7297 \r
7298   case WM_MOVING:\r
7299     return OnMoving( &sd, hDlg, wParam, lParam );\r
7300 \r
7301   case WM_EXITSIZEMOVE:\r
7302         UpdateICSWidth(hText);\r
7303     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7304   }\r
7305 \r
7306   return DefWindowProc(hDlg, message, wParam, lParam);\r
7307 }\r
7308 \r
7309 \r
7310 VOID\r
7311 ConsoleCreate()\r
7312 {\r
7313   HWND hCons;\r
7314   if (hwndConsole) return;\r
7315   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7316   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7317 }\r
7318 \r
7319 \r
7320 VOID\r
7321 ConsoleOutput(char* data, int length, int forceVisible)\r
7322 {\r
7323   HWND hText;\r
7324   int trim, exlen;\r
7325   char *p, *q;\r
7326   char buf[CO_MAX+1];\r
7327   POINT pEnd;\r
7328   RECT rect;\r
7329   static int delayLF = 0;\r
7330   CHARRANGE savesel, sel;\r
7331 \r
7332   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7333   p = data;\r
7334   q = buf;\r
7335   if (delayLF) {\r
7336     *q++ = '\r';\r
7337     *q++ = '\n';\r
7338     delayLF = 0;\r
7339   }\r
7340   while (length--) {\r
7341     if (*p == '\n') {\r
7342       if (*++p) {\r
7343         *q++ = '\r';\r
7344         *q++ = '\n';\r
7345       } else {\r
7346         delayLF = 1;\r
7347       }\r
7348     } else if (*p == '\007') {\r
7349        MyPlaySound(&sounds[(int)SoundBell]);\r
7350        p++;\r
7351     } else {\r
7352       *q++ = *p++;\r
7353     }\r
7354   }\r
7355   *q = NULLCHAR;\r
7356   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7357   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7358   /* Save current selection */\r
7359   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7360   exlen = GetWindowTextLength(hText);\r
7361   /* Find out whether current end of text is visible */\r
7362   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7363   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7364   /* Trim existing text if it's too long */\r
7365   if (exlen + (q - buf) > CO_MAX) {\r
7366     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7367     sel.cpMin = 0;\r
7368     sel.cpMax = trim;\r
7369     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7370     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7371     exlen -= trim;\r
7372     savesel.cpMin -= trim;\r
7373     savesel.cpMax -= trim;\r
7374     if (exlen < 0) exlen = 0;\r
7375     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7376     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7377   }\r
7378   /* Append the new text */\r
7379   sel.cpMin = exlen;\r
7380   sel.cpMax = exlen;\r
7381   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7382   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7383   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7384   if (forceVisible || exlen == 0 ||\r
7385       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7386        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7387     /* Scroll to make new end of text visible if old end of text\r
7388        was visible or new text is an echo of user typein */\r
7389     sel.cpMin = 9999999;\r
7390     sel.cpMax = 9999999;\r
7391     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7392     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7393     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7394     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7395   }\r
7396   if (savesel.cpMax == exlen || forceVisible) {\r
7397     /* Move insert point to new end of text if it was at the old\r
7398        end of text or if the new text is an echo of user typein */\r
7399     sel.cpMin = 9999999;\r
7400     sel.cpMax = 9999999;\r
7401     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7402   } else {\r
7403     /* Restore previous selection */\r
7404     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7405   }\r
7406   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7407 }\r
7408 \r
7409 /*---------*/\r
7410 \r
7411 \r
7412 void\r
7413 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7414 {\r
7415   char buf[100];\r
7416   char *str;\r
7417   COLORREF oldFg, oldBg;\r
7418   HFONT oldFont;\r
7419   RECT rect;\r
7420 \r
7421   if(copyNumber > 1)
7422     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7423 \r
7424   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7425   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7426   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7427 \r
7428   rect.left = x;\r
7429   rect.right = x + squareSize;\r
7430   rect.top  = y;\r
7431   rect.bottom = y + squareSize;\r
7432   str = buf;\r
7433 \r
7434   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7435                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7436              y, ETO_CLIPPED|ETO_OPAQUE,\r
7437              &rect, str, strlen(str), NULL);\r
7438 \r
7439   (void) SetTextColor(hdc, oldFg);\r
7440   (void) SetBkColor(hdc, oldBg);\r
7441   (void) SelectObject(hdc, oldFont);\r
7442 }\r
7443 \r
7444 void\r
7445 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7446               RECT *rect, char *color, char *flagFell)\r
7447 {\r
7448   char buf[100];\r
7449   char *str;\r
7450   COLORREF oldFg, oldBg;\r
7451   HFONT oldFont;\r
7452 \r
7453   if (appData.clockMode) {\r
7454     if (tinyLayout)\r
7455       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7456     else\r
7457       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7458     str = buf;\r
7459   } else {\r
7460     str = color;\r
7461   }\r
7462 \r
7463   if (highlight) {\r
7464     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7465     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7466   } else {\r
7467     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7468     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7469   }\r
7470   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7471 \r
7472   JAWS_SILENCE\r
7473 \r
7474   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7475              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7476              rect, str, strlen(str), NULL);\r
7477   if(logoHeight > 0 && appData.clockMode) {\r
7478       RECT r;\r
7479       str += strlen(color)+2;\r
7480       r.top = rect->top + logoHeight/2;\r
7481       r.left = rect->left;\r
7482       r.right = rect->right;\r
7483       r.bottom = rect->bottom;\r
7484       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7485                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7486                  &r, str, strlen(str), NULL);\r
7487   }\r
7488   (void) SetTextColor(hdc, oldFg);\r
7489   (void) SetBkColor(hdc, oldBg);\r
7490   (void) SelectObject(hdc, oldFont);\r
7491 }\r
7492 \r
7493 \r
7494 int\r
7495 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7496            OVERLAPPED *ovl)\r
7497 {\r
7498   int ok, err;\r
7499 \r
7500   /* [AS]  */\r
7501   if( count <= 0 ) {\r
7502     if (appData.debugMode) {\r
7503       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7504     }\r
7505 \r
7506     return ERROR_INVALID_USER_BUFFER;\r
7507   }\r
7508 \r
7509   ResetEvent(ovl->hEvent);\r
7510   ovl->Offset = ovl->OffsetHigh = 0;\r
7511   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7512   if (ok) {\r
7513     err = NO_ERROR;\r
7514   } else {\r
7515     err = GetLastError();\r
7516     if (err == ERROR_IO_PENDING) {\r
7517       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7518       if (ok)\r
7519         err = NO_ERROR;\r
7520       else\r
7521         err = GetLastError();\r
7522     }\r
7523   }\r
7524   return err;\r
7525 }\r
7526 \r
7527 int\r
7528 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7529             OVERLAPPED *ovl)\r
7530 {\r
7531   int ok, err;\r
7532 \r
7533   ResetEvent(ovl->hEvent);\r
7534   ovl->Offset = ovl->OffsetHigh = 0;\r
7535   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7536   if (ok) {\r
7537     err = NO_ERROR;\r
7538   } else {\r
7539     err = GetLastError();\r
7540     if (err == ERROR_IO_PENDING) {\r
7541       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7542       if (ok)\r
7543         err = NO_ERROR;\r
7544       else\r
7545         err = GetLastError();\r
7546     }\r
7547   }\r
7548   return err;\r
7549 }\r
7550 \r
7551 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7552 void CheckForInputBufferFull( InputSource * is )\r
7553 {\r
7554     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7555         /* Look for end of line */\r
7556         char * p = is->buf;\r
7557         \r
7558         while( p < is->next && *p != '\n' ) {\r
7559             p++;\r
7560         }\r
7561 \r
7562         if( p >= is->next ) {\r
7563             if (appData.debugMode) {\r
7564                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7565             }\r
7566 \r
7567             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7568             is->count = (DWORD) -1;\r
7569             is->next = is->buf;\r
7570         }\r
7571     }\r
7572 }\r
7573 \r
7574 DWORD\r
7575 InputThread(LPVOID arg)\r
7576 {\r
7577   InputSource *is;\r
7578   OVERLAPPED ovl;\r
7579 \r
7580   is = (InputSource *) arg;\r
7581   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7582   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7583   while (is->hThread != NULL) {\r
7584     is->error = DoReadFile(is->hFile, is->next,\r
7585                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7586                            &is->count, &ovl);\r
7587     if (is->error == NO_ERROR) {\r
7588       is->next += is->count;\r
7589     } else {\r
7590       if (is->error == ERROR_BROKEN_PIPE) {\r
7591         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7592         is->count = 0;\r
7593       } else {\r
7594         is->count = (DWORD) -1;\r
7595         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7596         break; \r
7597       }\r
7598     }\r
7599 \r
7600     CheckForInputBufferFull( is );\r
7601 \r
7602     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7603 \r
7604     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7605 \r
7606     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7607   }\r
7608 \r
7609   CloseHandle(ovl.hEvent);\r
7610   CloseHandle(is->hFile);\r
7611 \r
7612   if (appData.debugMode) {\r
7613     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7614   }\r
7615 \r
7616   return 0;\r
7617 }\r
7618 \r
7619 \r
7620 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7621 DWORD\r
7622 NonOvlInputThread(LPVOID arg)\r
7623 {\r
7624   InputSource *is;\r
7625   char *p, *q;\r
7626   int i;\r
7627   char prev;\r
7628 \r
7629   is = (InputSource *) arg;\r
7630   while (is->hThread != NULL) {\r
7631     is->error = ReadFile(is->hFile, is->next,\r
7632                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7633                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7634     if (is->error == NO_ERROR) {\r
7635       /* Change CRLF to LF */\r
7636       if (is->next > is->buf) {\r
7637         p = is->next - 1;\r
7638         i = is->count + 1;\r
7639       } else {\r
7640         p = is->next;\r
7641         i = is->count;\r
7642       }\r
7643       q = p;\r
7644       prev = NULLCHAR;\r
7645       while (i > 0) {\r
7646         if (prev == '\r' && *p == '\n') {\r
7647           *(q-1) = '\n';\r
7648           is->count--;\r
7649         } else { \r
7650           *q++ = *p;\r
7651         }\r
7652         prev = *p++;\r
7653         i--;\r
7654       }\r
7655       *q = NULLCHAR;\r
7656       is->next = q;\r
7657     } else {\r
7658       if (is->error == ERROR_BROKEN_PIPE) {\r
7659         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7660         is->count = 0; \r
7661       } else {\r
7662         is->count = (DWORD) -1;\r
7663       }\r
7664     }\r
7665 \r
7666     CheckForInputBufferFull( is );\r
7667 \r
7668     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7669 \r
7670     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7671 \r
7672     if (is->count < 0) break;  /* Quit on error */\r
7673   }\r
7674   CloseHandle(is->hFile);\r
7675   return 0;\r
7676 }\r
7677 \r
7678 DWORD\r
7679 SocketInputThread(LPVOID arg)\r
7680 {\r
7681   InputSource *is;\r
7682 \r
7683   is = (InputSource *) arg;\r
7684   while (is->hThread != NULL) {\r
7685     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7686     if ((int)is->count == SOCKET_ERROR) {\r
7687       is->count = (DWORD) -1;\r
7688       is->error = WSAGetLastError();\r
7689     } else {\r
7690       is->error = NO_ERROR;\r
7691       is->next += is->count;\r
7692       if (is->count == 0 && is->second == is) {\r
7693         /* End of file on stderr; quit with no message */\r
7694         break;\r
7695       }\r
7696     }\r
7697     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7698 \r
7699     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7700 \r
7701     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7702   }\r
7703   return 0;\r
7704 }\r
7705 \r
7706 VOID\r
7707 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7708 {\r
7709   InputSource *is;\r
7710 \r
7711   is = (InputSource *) lParam;\r
7712   if (is->lineByLine) {\r
7713     /* Feed in lines one by one */\r
7714     char *p = is->buf;\r
7715     char *q = p;\r
7716     while (q < is->next) {\r
7717       if (*q++ == '\n') {\r
7718         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7719         p = q;\r
7720       }\r
7721     }\r
7722     \r
7723     /* Move any partial line to the start of the buffer */\r
7724     q = is->buf;\r
7725     while (p < is->next) {\r
7726       *q++ = *p++;\r
7727     }\r
7728     is->next = q;\r
7729 \r
7730     if (is->error != NO_ERROR || is->count == 0) {\r
7731       /* Notify backend of the error.  Note: If there was a partial\r
7732          line at the end, it is not flushed through. */\r
7733       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7734     }\r
7735   } else {\r
7736     /* Feed in the whole chunk of input at once */\r
7737     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7738     is->next = is->buf;\r
7739   }\r
7740 }\r
7741 \r
7742 /*---------------------------------------------------------------------------*\\r
7743  *\r
7744  *  Menu enables. Used when setting various modes.\r
7745  *\r
7746 \*---------------------------------------------------------------------------*/\r
7747 \r
7748 typedef struct {\r
7749   int item;\r
7750   int flags;\r
7751 } Enables;\r
7752 \r
7753 VOID\r
7754 GreyRevert(Boolean grey)\r
7755 { // [HGM] vari: for retracting variations in local mode\r
7756   HMENU hmenu = GetMenu(hwndMain);\r
7757   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7758   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7759 }\r
7760 \r
7761 VOID\r
7762 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7763 {\r
7764   while (enab->item > 0) {\r
7765     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7766     enab++;\r
7767   }\r
7768 }\r
7769 \r
7770 Enables gnuEnables[] = {\r
7771   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7784   { -1, -1 }\r
7785 };\r
7786 \r
7787 Enables icsEnables[] = {\r
7788   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7796   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7802   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7805   { -1, -1 }\r
7806 };\r
7807 \r
7808 #if ZIPPY\r
7809 Enables zippyEnables[] = {\r
7810   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7811   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7812   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7813   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7814   { -1, -1 }\r
7815 };\r
7816 #endif\r
7817 \r
7818 Enables ncpEnables[] = {\r
7819   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7828   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7829   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7841   { -1, -1 }\r
7842 };\r
7843 \r
7844 Enables trainingOnEnables[] = {\r
7845   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7854   { -1, -1 }\r
7855 };\r
7856 \r
7857 Enables trainingOffEnables[] = {\r
7858   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7867   { -1, -1 }\r
7868 };\r
7869 \r
7870 /* These modify either ncpEnables or gnuEnables */\r
7871 Enables cmailEnables[] = {\r
7872   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7874   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7875   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7877   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7879   { -1, -1 }\r
7880 };\r
7881 \r
7882 Enables machineThinkingEnables[] = {\r
7883   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7899   { -1, -1 }\r
7900 };\r
7901 \r
7902 Enables userThinkingEnables[] = {\r
7903   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7919   { -1, -1 }\r
7920 };\r
7921 \r
7922 /*---------------------------------------------------------------------------*\\r
7923  *\r
7924  *  Front-end interface functions exported by XBoard.\r
7925  *  Functions appear in same order as prototypes in frontend.h.\r
7926  * \r
7927 \*---------------------------------------------------------------------------*/\r
7928 VOID\r
7929 ModeHighlight()\r
7930 {\r
7931   static UINT prevChecked = 0;\r
7932   static int prevPausing = 0;\r
7933   UINT nowChecked;\r
7934 \r
7935   if (pausing != prevPausing) {\r
7936     prevPausing = pausing;\r
7937     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7938                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7939     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7940   }\r
7941 \r
7942   switch (gameMode) {\r
7943   case BeginningOfGame:\r
7944     if (appData.icsActive)\r
7945       nowChecked = IDM_IcsClient;\r
7946     else if (appData.noChessProgram)\r
7947       nowChecked = IDM_EditGame;\r
7948     else\r
7949       nowChecked = IDM_MachineBlack;\r
7950     break;\r
7951   case MachinePlaysBlack:\r
7952     nowChecked = IDM_MachineBlack;\r
7953     break;\r
7954   case MachinePlaysWhite:\r
7955     nowChecked = IDM_MachineWhite;\r
7956     break;\r
7957   case TwoMachinesPlay:\r
7958     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7959     break;\r
7960   case AnalyzeMode:\r
7961     nowChecked = IDM_AnalysisMode;\r
7962     break;\r
7963   case AnalyzeFile:\r
7964     nowChecked = IDM_AnalyzeFile;\r
7965     break;\r
7966   case EditGame:\r
7967     nowChecked = IDM_EditGame;\r
7968     break;\r
7969   case PlayFromGameFile:\r
7970     nowChecked = IDM_LoadGame;\r
7971     break;\r
7972   case EditPosition:\r
7973     nowChecked = IDM_EditPosition;\r
7974     break;\r
7975   case Training:\r
7976     nowChecked = IDM_Training;\r
7977     break;\r
7978   case IcsPlayingWhite:\r
7979   case IcsPlayingBlack:\r
7980   case IcsObserving:\r
7981   case IcsIdle:\r
7982     nowChecked = IDM_IcsClient;\r
7983     break;\r
7984   default:\r
7985   case EndOfGame:\r
7986     nowChecked = 0;\r
7987     break;\r
7988   }\r
7989   if (prevChecked != 0)\r
7990     (void) CheckMenuItem(GetMenu(hwndMain),\r
7991                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7992   if (nowChecked != 0)\r
7993     (void) CheckMenuItem(GetMenu(hwndMain),\r
7994                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7995 \r
7996   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7997     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7998                           MF_BYCOMMAND|MF_ENABLED);\r
7999   } else {\r
8000     (void) EnableMenuItem(GetMenu(hwndMain), \r
8001                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8002   }\r
8003 \r
8004   prevChecked = nowChecked;\r
8005 \r
8006   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8007   if (appData.icsActive) {\r
8008        if (appData.icsEngineAnalyze) {\r
8009                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8010                        MF_BYCOMMAND|MF_CHECKED);\r
8011        } else {\r
8012                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8013                        MF_BYCOMMAND|MF_UNCHECKED);\r
8014        }\r
8015   }\r
8016   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8017 }\r
8018 \r
8019 VOID\r
8020 SetICSMode()\r
8021 {\r
8022   HMENU hmenu = GetMenu(hwndMain);\r
8023   SetMenuEnables(hmenu, icsEnables);\r
8024   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8025     MF_BYCOMMAND|MF_ENABLED);\r
8026 #if ZIPPY\r
8027   if (appData.zippyPlay) {\r
8028     SetMenuEnables(hmenu, zippyEnables);\r
8029     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8030          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8031           MF_BYCOMMAND|MF_ENABLED);\r
8032   }\r
8033 #endif\r
8034 }\r
8035 \r
8036 VOID\r
8037 SetGNUMode()\r
8038 {\r
8039   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8040 }\r
8041 \r
8042 VOID\r
8043 SetNCPMode()\r
8044 {\r
8045   HMENU hmenu = GetMenu(hwndMain);\r
8046   SetMenuEnables(hmenu, ncpEnables);\r
8047     DrawMenuBar(hwndMain);\r
8048 }\r
8049 \r
8050 VOID\r
8051 SetCmailMode()\r
8052 {\r
8053   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8054 }\r
8055 \r
8056 VOID \r
8057 SetTrainingModeOn()\r
8058 {\r
8059   int i;\r
8060   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8061   for (i = 0; i < N_BUTTONS; i++) {\r
8062     if (buttonDesc[i].hwnd != NULL)\r
8063       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8064   }\r
8065   CommentPopDown();\r
8066 }\r
8067 \r
8068 VOID SetTrainingModeOff()\r
8069 {\r
8070   int i;\r
8071   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8072   for (i = 0; i < N_BUTTONS; i++) {\r
8073     if (buttonDesc[i].hwnd != NULL)\r
8074       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8075   }\r
8076 }\r
8077 \r
8078 \r
8079 VOID\r
8080 SetUserThinkingEnables()\r
8081 {\r
8082   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8083 }\r
8084 \r
8085 VOID\r
8086 SetMachineThinkingEnables()\r
8087 {\r
8088   HMENU hMenu = GetMenu(hwndMain);\r
8089   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8090 \r
8091   SetMenuEnables(hMenu, machineThinkingEnables);\r
8092 \r
8093   if (gameMode == MachinePlaysBlack) {\r
8094     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8095   } else if (gameMode == MachinePlaysWhite) {\r
8096     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8097   } else if (gameMode == TwoMachinesPlay) {\r
8098     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8099   }\r
8100 }\r
8101 \r
8102 \r
8103 VOID\r
8104 DisplayTitle(char *str)\r
8105 {\r
8106   char title[MSG_SIZ], *host;\r
8107   if (str[0] != NULLCHAR) {\r
8108     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8109   } else if (appData.icsActive) {\r
8110     if (appData.icsCommPort[0] != NULLCHAR)\r
8111       host = "ICS";\r
8112     else \r
8113       host = appData.icsHost;\r
8114       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8115   } else if (appData.noChessProgram) {\r
8116     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8117   } else {\r
8118     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8119     strcat(title, ": ");\r
8120     strcat(title, first.tidy);\r
8121   }\r
8122   SetWindowText(hwndMain, title);\r
8123 }\r
8124 \r
8125 \r
8126 VOID\r
8127 DisplayMessage(char *str1, char *str2)\r
8128 {\r
8129   HDC hdc;\r
8130   HFONT oldFont;\r
8131   int remain = MESSAGE_TEXT_MAX - 1;\r
8132   int len;\r
8133 \r
8134   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8135   messageText[0] = NULLCHAR;\r
8136   if (*str1) {\r
8137     len = strlen(str1);\r
8138     if (len > remain) len = remain;\r
8139     strncpy(messageText, str1, len);\r
8140     messageText[len] = NULLCHAR;\r
8141     remain -= len;\r
8142   }\r
8143   if (*str2 && remain >= 2) {\r
8144     if (*str1) {\r
8145       strcat(messageText, "  ");\r
8146       remain -= 2;\r
8147     }\r
8148     len = strlen(str2);\r
8149     if (len > remain) len = remain;\r
8150     strncat(messageText, str2, len);\r
8151   }\r
8152   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8153 \r
8154   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8155 \r
8156   SAYMACHINEMOVE();\r
8157 \r
8158   hdc = GetDC(hwndMain);\r
8159   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8160   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8161              &messageRect, messageText, strlen(messageText), NULL);\r
8162   (void) SelectObject(hdc, oldFont);\r
8163   (void) ReleaseDC(hwndMain, hdc);\r
8164 }\r
8165 \r
8166 VOID\r
8167 DisplayError(char *str, int error)\r
8168 {\r
8169   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8170   int len;\r
8171 \r
8172   if (error == 0) {\r
8173     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8174   } else {\r
8175     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8176                         NULL, error, LANG_NEUTRAL,\r
8177                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8178     if (len > 0) {\r
8179       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8180     } else {\r
8181       ErrorMap *em = errmap;\r
8182       while (em->err != 0 && em->err != error) em++;\r
8183       if (em->err != 0) {\r
8184         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8185       } else {\r
8186         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8187       }\r
8188     }\r
8189   }\r
8190   \r
8191   ErrorPopUp(_("Error"), buf);\r
8192 }\r
8193 \r
8194 \r
8195 VOID\r
8196 DisplayMoveError(char *str)\r
8197 {\r
8198   fromX = fromY = -1;\r
8199   ClearHighlights();\r
8200   DrawPosition(FALSE, NULL);\r
8201   if (appData.popupMoveErrors) {\r
8202     ErrorPopUp(_("Error"), str);\r
8203   } else {\r
8204     DisplayMessage(str, "");\r
8205     moveErrorMessageUp = TRUE;\r
8206   }\r
8207 }\r
8208 \r
8209 VOID\r
8210 DisplayFatalError(char *str, int error, int exitStatus)\r
8211 {\r
8212   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8213   int len;\r
8214   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8215 \r
8216   if (error != 0) {\r
8217     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8218                         NULL, error, LANG_NEUTRAL,\r
8219                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8220     if (len > 0) {\r
8221       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8222     } else {\r
8223       ErrorMap *em = errmap;\r
8224       while (em->err != 0 && em->err != error) em++;\r
8225       if (em->err != 0) {\r
8226         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8227       } else {\r
8228         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8229       }\r
8230     }\r
8231     str = buf;\r
8232   }\r
8233   if (appData.debugMode) {\r
8234     fprintf(debugFP, "%s: %s\n", label, str);\r
8235   }\r
8236   if (appData.popupExitMessage) {\r
8237     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8238                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8239   }\r
8240   ExitEvent(exitStatus);\r
8241 }\r
8242 \r
8243 \r
8244 VOID\r
8245 DisplayInformation(char *str)\r
8246 {\r
8247   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8248 }\r
8249 \r
8250 \r
8251 VOID\r
8252 DisplayNote(char *str)\r
8253 {\r
8254   ErrorPopUp(_("Note"), str);\r
8255 }\r
8256 \r
8257 \r
8258 typedef struct {\r
8259   char *title, *question, *replyPrefix;\r
8260   ProcRef pr;\r
8261 } QuestionParams;\r
8262 \r
8263 LRESULT CALLBACK\r
8264 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8265 {\r
8266   static QuestionParams *qp;\r
8267   char reply[MSG_SIZ];\r
8268   int len, err;\r
8269 \r
8270   switch (message) {\r
8271   case WM_INITDIALOG:\r
8272     qp = (QuestionParams *) lParam;\r
8273     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8274     Translate(hDlg, DLG_Question);\r
8275     SetWindowText(hDlg, qp->title);\r
8276     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8277     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8278     return FALSE;\r
8279 \r
8280   case WM_COMMAND:\r
8281     switch (LOWORD(wParam)) {\r
8282     case IDOK:\r
8283       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8284       if (*reply) strcat(reply, " ");\r
8285       len = strlen(reply);\r
8286       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8287       strcat(reply, "\n");\r
8288       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8289       EndDialog(hDlg, TRUE);\r
8290       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8291       return TRUE;\r
8292     case IDCANCEL:\r
8293       EndDialog(hDlg, FALSE);\r
8294       return TRUE;\r
8295     default:\r
8296       break;\r
8297     }\r
8298     break;\r
8299   }\r
8300   return FALSE;\r
8301 }\r
8302 \r
8303 VOID\r
8304 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8305 {\r
8306     QuestionParams qp;\r
8307     FARPROC lpProc;\r
8308     \r
8309     qp.title = title;\r
8310     qp.question = question;\r
8311     qp.replyPrefix = replyPrefix;\r
8312     qp.pr = pr;\r
8313     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8314     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8315       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8316     FreeProcInstance(lpProc);\r
8317 }\r
8318 \r
8319 /* [AS] Pick FRC position */\r
8320 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8321 {\r
8322     static int * lpIndexFRC;\r
8323     BOOL index_is_ok;\r
8324     char buf[16];\r
8325 \r
8326     switch( message )\r
8327     {\r
8328     case WM_INITDIALOG:\r
8329         lpIndexFRC = (int *) lParam;\r
8330 \r
8331         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8332         Translate(hDlg, DLG_NewGameFRC);\r
8333 \r
8334         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8335         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8336         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8337         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8338 \r
8339         break;\r
8340 \r
8341     case WM_COMMAND:\r
8342         switch( LOWORD(wParam) ) {\r
8343         case IDOK:\r
8344             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8345             EndDialog( hDlg, 0 );\r
8346             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8347             return TRUE;\r
8348         case IDCANCEL:\r
8349             EndDialog( hDlg, 1 );   \r
8350             return TRUE;\r
8351         case IDC_NFG_Edit:\r
8352             if( HIWORD(wParam) == EN_CHANGE ) {\r
8353                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8354 \r
8355                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8356             }\r
8357             return TRUE;\r
8358         case IDC_NFG_Random:\r
8359           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8360             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8361             return TRUE;\r
8362         }\r
8363 \r
8364         break;\r
8365     }\r
8366 \r
8367     return FALSE;\r
8368 }\r
8369 \r
8370 int NewGameFRC()\r
8371 {\r
8372     int result;\r
8373     int index = appData.defaultFrcPosition;\r
8374     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8375 \r
8376     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8377 \r
8378     if( result == 0 ) {\r
8379         appData.defaultFrcPosition = index;\r
8380     }\r
8381 \r
8382     return result;\r
8383 }\r
8384 \r
8385 /* [AS] Game list options. Refactored by HGM */\r
8386 \r
8387 HWND gameListOptionsDialog;\r
8388 \r
8389 // low-level front-end: clear text edit / list widget\r
8390 void\r
8391 GLT_ClearList()\r
8392 {\r
8393     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8394 }\r
8395 \r
8396 // low-level front-end: clear text edit / list widget\r
8397 void\r
8398 GLT_DeSelectList()\r
8399 {\r
8400     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8401 }\r
8402 \r
8403 // low-level front-end: append line to text edit / list widget\r
8404 void\r
8405 GLT_AddToList( char *name )\r
8406 {\r
8407     if( name != 0 ) {\r
8408             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8409     }\r
8410 }\r
8411 \r
8412 // low-level front-end: get line from text edit / list widget\r
8413 Boolean\r
8414 GLT_GetFromList( int index, char *name )\r
8415 {\r
8416     if( name != 0 ) {\r
8417             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8418                 return TRUE;\r
8419     }\r
8420     return FALSE;\r
8421 }\r
8422 \r
8423 void GLT_MoveSelection( HWND hDlg, int delta )\r
8424 {\r
8425     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8426     int idx2 = idx1 + delta;\r
8427     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8428 \r
8429     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8430         char buf[128];\r
8431 \r
8432         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8433         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8434         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8435         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8436     }\r
8437 }\r
8438 \r
8439 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8440 {\r
8441     switch( message )\r
8442     {\r
8443     case WM_INITDIALOG:\r
8444         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8445         \r
8446         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8447         Translate(hDlg, DLG_GameListOptions);\r
8448 \r
8449         /* Initialize list */\r
8450         GLT_TagsToList( lpUserGLT );\r
8451 \r
8452         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8453 \r
8454         break;\r
8455 \r
8456     case WM_COMMAND:\r
8457         switch( LOWORD(wParam) ) {\r
8458         case IDOK:\r
8459             GLT_ParseList();\r
8460             EndDialog( hDlg, 0 );\r
8461             return TRUE;\r
8462         case IDCANCEL:\r
8463             EndDialog( hDlg, 1 );\r
8464             return TRUE;\r
8465 \r
8466         case IDC_GLT_Default:\r
8467             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8468             return TRUE;\r
8469 \r
8470         case IDC_GLT_Restore:\r
8471             GLT_TagsToList( appData.gameListTags );\r
8472             return TRUE;\r
8473 \r
8474         case IDC_GLT_Up:\r
8475             GLT_MoveSelection( hDlg, -1 );\r
8476             return TRUE;\r
8477 \r
8478         case IDC_GLT_Down:\r
8479             GLT_MoveSelection( hDlg, +1 );\r
8480             return TRUE;\r
8481         }\r
8482 \r
8483         break;\r
8484     }\r
8485 \r
8486     return FALSE;\r
8487 }\r
8488 \r
8489 int GameListOptions()\r
8490 {\r
8491     int result;\r
8492     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8493 \r
8494       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8495 \r
8496     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8497 \r
8498     if( result == 0 ) {\r
8499         /* [AS] Memory leak here! */\r
8500         appData.gameListTags = strdup( lpUserGLT ); \r
8501     }\r
8502 \r
8503     return result;\r
8504 }\r
8505 \r
8506 VOID\r
8507 DisplayIcsInteractionTitle(char *str)\r
8508 {\r
8509   char consoleTitle[MSG_SIZ];\r
8510 \r
8511     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8512   SetWindowText(hwndConsole, consoleTitle);\r
8513 }\r
8514 \r
8515 void\r
8516 DrawPosition(int fullRedraw, Board board)\r
8517 {\r
8518   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8519 }\r
8520 \r
8521 void NotifyFrontendLogin()\r
8522 {\r
8523         if (hwndConsole)\r
8524                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8525 }\r
8526 \r
8527 VOID\r
8528 ResetFrontEnd()\r
8529 {\r
8530   fromX = fromY = -1;\r
8531   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8532     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8533     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8534     dragInfo.lastpos = dragInfo.pos;\r
8535     dragInfo.start.x = dragInfo.start.y = -1;\r
8536     dragInfo.from = dragInfo.start;\r
8537     ReleaseCapture();\r
8538     DrawPosition(TRUE, NULL);\r
8539   }\r
8540   TagsPopDown();\r
8541 }\r
8542 \r
8543 \r
8544 VOID\r
8545 CommentPopUp(char *title, char *str)\r
8546 {\r
8547   HWND hwnd = GetActiveWindow();\r
8548   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8549   SAY(str);\r
8550   SetActiveWindow(hwnd);\r
8551 }\r
8552 \r
8553 VOID\r
8554 CommentPopDown(void)\r
8555 {\r
8556   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8557   if (commentDialog) {\r
8558     ShowWindow(commentDialog, SW_HIDE);\r
8559   }\r
8560   commentUp = FALSE;\r
8561 }\r
8562 \r
8563 VOID\r
8564 EditCommentPopUp(int index, char *title, char *str)\r
8565 {\r
8566   EitherCommentPopUp(index, title, str, TRUE);\r
8567 }\r
8568 \r
8569 \r
8570 VOID\r
8571 RingBell()\r
8572 {\r
8573   MyPlaySound(&sounds[(int)SoundMove]);\r
8574 }\r
8575 \r
8576 VOID PlayIcsWinSound()\r
8577 {\r
8578   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8579 }\r
8580 \r
8581 VOID PlayIcsLossSound()\r
8582 {\r
8583   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8584 }\r
8585 \r
8586 VOID PlayIcsDrawSound()\r
8587 {\r
8588   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8589 }\r
8590 \r
8591 VOID PlayIcsUnfinishedSound()\r
8592 {\r
8593   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8594 }\r
8595 \r
8596 VOID\r
8597 PlayAlarmSound()\r
8598 {\r
8599   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8600 }\r
8601 \r
8602 \r
8603 VOID\r
8604 EchoOn()\r
8605 {\r
8606   HWND hInput;\r
8607   consoleEcho = TRUE;\r
8608   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8609   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8610   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8611 }\r
8612 \r
8613 \r
8614 VOID\r
8615 EchoOff()\r
8616 {\r
8617   CHARFORMAT cf;\r
8618   HWND hInput;\r
8619   consoleEcho = FALSE;\r
8620   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8621   /* This works OK: set text and background both to the same color */\r
8622   cf = consoleCF;\r
8623   cf.crTextColor = COLOR_ECHOOFF;\r
8624   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8625   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8626 }\r
8627 \r
8628 /* No Raw()...? */\r
8629 \r
8630 void Colorize(ColorClass cc, int continuation)\r
8631 {\r
8632   currentColorClass = cc;\r
8633   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8634   consoleCF.crTextColor = textAttribs[cc].color;\r
8635   consoleCF.dwEffects = textAttribs[cc].effects;\r
8636   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8637 }\r
8638 \r
8639 char *\r
8640 UserName()\r
8641 {\r
8642   static char buf[MSG_SIZ];\r
8643   DWORD bufsiz = MSG_SIZ;\r
8644 \r
8645   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8646         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8647   }\r
8648   if (!GetUserName(buf, &bufsiz)) {\r
8649     /*DisplayError("Error getting user name", GetLastError());*/\r
8650     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8651   }\r
8652   return buf;\r
8653 }\r
8654 \r
8655 char *\r
8656 HostName()\r
8657 {\r
8658   static char buf[MSG_SIZ];\r
8659   DWORD bufsiz = MSG_SIZ;\r
8660 \r
8661   if (!GetComputerName(buf, &bufsiz)) {\r
8662     /*DisplayError("Error getting host name", GetLastError());*/\r
8663     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8664   }\r
8665   return buf;\r
8666 }\r
8667 \r
8668 \r
8669 int\r
8670 ClockTimerRunning()\r
8671 {\r
8672   return clockTimerEvent != 0;\r
8673 }\r
8674 \r
8675 int\r
8676 StopClockTimer()\r
8677 {\r
8678   if (clockTimerEvent == 0) return FALSE;\r
8679   KillTimer(hwndMain, clockTimerEvent);\r
8680   clockTimerEvent = 0;\r
8681   return TRUE;\r
8682 }\r
8683 \r
8684 void\r
8685 StartClockTimer(long millisec)\r
8686 {\r
8687   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8688                              (UINT) millisec, NULL);\r
8689 }\r
8690 \r
8691 void\r
8692 DisplayWhiteClock(long timeRemaining, int highlight)\r
8693 {\r
8694   HDC hdc;\r
8695   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8696 \r
8697   if(appData.noGUI) return;\r
8698   hdc = GetDC(hwndMain);\r
8699   if (!IsIconic(hwndMain)) {\r
8700     DisplayAClock(hdc, timeRemaining, highlight, \r
8701                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8702   }\r
8703   if (highlight && iconCurrent == iconBlack) {\r
8704     iconCurrent = iconWhite;\r
8705     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8706     if (IsIconic(hwndMain)) {\r
8707       DrawIcon(hdc, 2, 2, iconCurrent);\r
8708     }\r
8709   }\r
8710   (void) ReleaseDC(hwndMain, hdc);\r
8711   if (hwndConsole)\r
8712     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8713 }\r
8714 \r
8715 void\r
8716 DisplayBlackClock(long timeRemaining, int highlight)\r
8717 {\r
8718   HDC hdc;\r
8719   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8720 \r
8721   if(appData.noGUI) return;\r
8722   hdc = GetDC(hwndMain);\r
8723   if (!IsIconic(hwndMain)) {\r
8724     DisplayAClock(hdc, timeRemaining, highlight, \r
8725                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8726   }\r
8727   if (highlight && iconCurrent == iconWhite) {\r
8728     iconCurrent = iconBlack;\r
8729     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8730     if (IsIconic(hwndMain)) {\r
8731       DrawIcon(hdc, 2, 2, iconCurrent);\r
8732     }\r
8733   }\r
8734   (void) ReleaseDC(hwndMain, hdc);\r
8735   if (hwndConsole)\r
8736     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8737 }\r
8738 \r
8739 \r
8740 int\r
8741 LoadGameTimerRunning()\r
8742 {\r
8743   return loadGameTimerEvent != 0;\r
8744 }\r
8745 \r
8746 int\r
8747 StopLoadGameTimer()\r
8748 {\r
8749   if (loadGameTimerEvent == 0) return FALSE;\r
8750   KillTimer(hwndMain, loadGameTimerEvent);\r
8751   loadGameTimerEvent = 0;\r
8752   return TRUE;\r
8753 }\r
8754 \r
8755 void\r
8756 StartLoadGameTimer(long millisec)\r
8757 {\r
8758   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8759                                 (UINT) millisec, NULL);\r
8760 }\r
8761 \r
8762 void\r
8763 AutoSaveGame()\r
8764 {\r
8765   char *defName;\r
8766   FILE *f;\r
8767   char fileTitle[MSG_SIZ];\r
8768 \r
8769   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8770   f = OpenFileDialog(hwndMain, "a", defName,\r
8771                      appData.oldSaveStyle ? "gam" : "pgn",\r
8772                      GAME_FILT, \r
8773                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8774   if (f != NULL) {\r
8775     SaveGame(f, 0, "");\r
8776     fclose(f);\r
8777   }\r
8778 }\r
8779 \r
8780 \r
8781 void\r
8782 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8783 {\r
8784   if (delayedTimerEvent != 0) {\r
8785     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8786       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8787     }\r
8788     KillTimer(hwndMain, delayedTimerEvent);\r
8789     delayedTimerEvent = 0;\r
8790     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8791     delayedTimerCallback();\r
8792   }\r
8793   delayedTimerCallback = cb;\r
8794   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8795                                 (UINT) millisec, NULL);\r
8796 }\r
8797 \r
8798 DelayedEventCallback\r
8799 GetDelayedEvent()\r
8800 {\r
8801   if (delayedTimerEvent) {\r
8802     return delayedTimerCallback;\r
8803   } else {\r
8804     return NULL;\r
8805   }\r
8806 }\r
8807 \r
8808 void\r
8809 CancelDelayedEvent()\r
8810 {\r
8811   if (delayedTimerEvent) {\r
8812     KillTimer(hwndMain, delayedTimerEvent);\r
8813     delayedTimerEvent = 0;\r
8814   }\r
8815 }\r
8816 \r
8817 DWORD GetWin32Priority(int nice)\r
8818 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8819 /*\r
8820 REALTIME_PRIORITY_CLASS     0x00000100\r
8821 HIGH_PRIORITY_CLASS         0x00000080\r
8822 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8823 NORMAL_PRIORITY_CLASS       0x00000020\r
8824 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8825 IDLE_PRIORITY_CLASS         0x00000040\r
8826 */\r
8827         if (nice < -15) return 0x00000080;\r
8828         if (nice < 0)   return 0x00008000;\r
8829         if (nice == 0)  return 0x00000020;\r
8830         if (nice < 15)  return 0x00004000;\r
8831         return 0x00000040;\r
8832 }\r
8833 \r
8834 /* Start a child process running the given program.\r
8835    The process's standard output can be read from "from", and its\r
8836    standard input can be written to "to".\r
8837    Exit with fatal error if anything goes wrong.\r
8838    Returns an opaque pointer that can be used to destroy the process\r
8839    later.\r
8840 */\r
8841 int\r
8842 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8843 {\r
8844 #define BUFSIZE 4096\r
8845 \r
8846   HANDLE hChildStdinRd, hChildStdinWr,\r
8847     hChildStdoutRd, hChildStdoutWr;\r
8848   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8849   SECURITY_ATTRIBUTES saAttr;\r
8850   BOOL fSuccess;\r
8851   PROCESS_INFORMATION piProcInfo;\r
8852   STARTUPINFO siStartInfo;\r
8853   ChildProc *cp;\r
8854   char buf[MSG_SIZ];\r
8855   DWORD err;\r
8856 \r
8857   if (appData.debugMode) {\r
8858     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8859   }\r
8860 \r
8861   *pr = NoProc;\r
8862 \r
8863   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8864   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8865   saAttr.bInheritHandle = TRUE;\r
8866   saAttr.lpSecurityDescriptor = NULL;\r
8867 \r
8868   /*\r
8869    * The steps for redirecting child's STDOUT:\r
8870    *     1. Create anonymous pipe to be STDOUT for child.\r
8871    *     2. Create a noninheritable duplicate of read handle,\r
8872    *         and close the inheritable read handle.\r
8873    */\r
8874 \r
8875   /* Create a pipe for the child's STDOUT. */\r
8876   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8877     return GetLastError();\r
8878   }\r
8879 \r
8880   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8881   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8882                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8883                              FALSE,     /* not inherited */\r
8884                              DUPLICATE_SAME_ACCESS);\r
8885   if (! fSuccess) {\r
8886     return GetLastError();\r
8887   }\r
8888   CloseHandle(hChildStdoutRd);\r
8889 \r
8890   /*\r
8891    * The steps for redirecting child's STDIN:\r
8892    *     1. Create anonymous pipe to be STDIN for child.\r
8893    *     2. Create a noninheritable duplicate of write handle,\r
8894    *         and close the inheritable write handle.\r
8895    */\r
8896 \r
8897   /* Create a pipe for the child's STDIN. */\r
8898   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8899     return GetLastError();\r
8900   }\r
8901 \r
8902   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8903   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8904                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8905                              FALSE,     /* not inherited */\r
8906                              DUPLICATE_SAME_ACCESS);\r
8907   if (! fSuccess) {\r
8908     return GetLastError();\r
8909   }\r
8910   CloseHandle(hChildStdinWr);\r
8911 \r
8912   /* Arrange to (1) look in dir for the child .exe file, and\r
8913    * (2) have dir be the child's working directory.  Interpret\r
8914    * dir relative to the directory WinBoard loaded from. */\r
8915   GetCurrentDirectory(MSG_SIZ, buf);\r
8916   SetCurrentDirectory(installDir);\r
8917   SetCurrentDirectory(dir);\r
8918 \r
8919   /* Now create the child process. */\r
8920 \r
8921   siStartInfo.cb = sizeof(STARTUPINFO);\r
8922   siStartInfo.lpReserved = NULL;\r
8923   siStartInfo.lpDesktop = NULL;\r
8924   siStartInfo.lpTitle = NULL;\r
8925   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8926   siStartInfo.cbReserved2 = 0;\r
8927   siStartInfo.lpReserved2 = NULL;\r
8928   siStartInfo.hStdInput = hChildStdinRd;\r
8929   siStartInfo.hStdOutput = hChildStdoutWr;\r
8930   siStartInfo.hStdError = hChildStdoutWr;\r
8931 \r
8932   fSuccess = CreateProcess(NULL,\r
8933                            cmdLine,        /* command line */\r
8934                            NULL,           /* process security attributes */\r
8935                            NULL,           /* primary thread security attrs */\r
8936                            TRUE,           /* handles are inherited */\r
8937                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8938                            NULL,           /* use parent's environment */\r
8939                            NULL,\r
8940                            &siStartInfo, /* STARTUPINFO pointer */\r
8941                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8942 \r
8943   err = GetLastError();\r
8944   SetCurrentDirectory(buf); /* return to prev directory */\r
8945   if (! fSuccess) {\r
8946     return err;\r
8947   }\r
8948 \r
8949   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8950     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8951     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8952   }\r
8953 \r
8954   /* Close the handles we don't need in the parent */\r
8955   CloseHandle(piProcInfo.hThread);\r
8956   CloseHandle(hChildStdinRd);\r
8957   CloseHandle(hChildStdoutWr);\r
8958 \r
8959   /* Prepare return value */\r
8960   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8961   cp->kind = CPReal;\r
8962   cp->hProcess = piProcInfo.hProcess;\r
8963   cp->pid = piProcInfo.dwProcessId;\r
8964   cp->hFrom = hChildStdoutRdDup;\r
8965   cp->hTo = hChildStdinWrDup;\r
8966 \r
8967   *pr = (void *) cp;\r
8968 \r
8969   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8970      2000 where engines sometimes don't see the initial command(s)\r
8971      from WinBoard and hang.  I don't understand how that can happen,\r
8972      but the Sleep is harmless, so I've put it in.  Others have also\r
8973      reported what may be the same problem, so hopefully this will fix\r
8974      it for them too.  */\r
8975   Sleep(500);\r
8976 \r
8977   return NO_ERROR;\r
8978 }\r
8979 \r
8980 \r
8981 void\r
8982 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8983 {\r
8984   ChildProc *cp; int result;\r
8985 \r
8986   cp = (ChildProc *) pr;\r
8987   if (cp == NULL) return;\r
8988 \r
8989   switch (cp->kind) {\r
8990   case CPReal:\r
8991     /* TerminateProcess is considered harmful, so... */\r
8992     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8993     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8994     /* The following doesn't work because the chess program\r
8995        doesn't "have the same console" as WinBoard.  Maybe\r
8996        we could arrange for this even though neither WinBoard\r
8997        nor the chess program uses a console for stdio? */\r
8998     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8999 \r
9000     /* [AS] Special termination modes for misbehaving programs... */\r
9001     if( signal == 9 ) { \r
9002         result = TerminateProcess( cp->hProcess, 0 );\r
9003 \r
9004         if ( appData.debugMode) {\r
9005             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9006         }\r
9007     }\r
9008     else if( signal == 10 ) {\r
9009         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9010 \r
9011         if( dw != WAIT_OBJECT_0 ) {\r
9012             result = TerminateProcess( cp->hProcess, 0 );\r
9013 \r
9014             if ( appData.debugMode) {\r
9015                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9016             }\r
9017 \r
9018         }\r
9019     }\r
9020 \r
9021     CloseHandle(cp->hProcess);\r
9022     break;\r
9023 \r
9024   case CPComm:\r
9025     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9026     break;\r
9027 \r
9028   case CPSock:\r
9029     closesocket(cp->sock);\r
9030     WSACleanup();\r
9031     break;\r
9032 \r
9033   case CPRcmd:\r
9034     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9035     closesocket(cp->sock);\r
9036     closesocket(cp->sock2);\r
9037     WSACleanup();\r
9038     break;\r
9039   }\r
9040   free(cp);\r
9041 }\r
9042 \r
9043 void\r
9044 InterruptChildProcess(ProcRef pr)\r
9045 {\r
9046   ChildProc *cp;\r
9047 \r
9048   cp = (ChildProc *) pr;\r
9049   if (cp == NULL) return;\r
9050   switch (cp->kind) {\r
9051   case CPReal:\r
9052     /* The following doesn't work because the chess program\r
9053        doesn't "have the same console" as WinBoard.  Maybe\r
9054        we could arrange for this even though neither WinBoard\r
9055        nor the chess program uses a console for stdio */\r
9056     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9057     break;\r
9058 \r
9059   case CPComm:\r
9060   case CPSock:\r
9061     /* Can't interrupt */\r
9062     break;\r
9063 \r
9064   case CPRcmd:\r
9065     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9066     break;\r
9067   }\r
9068 }\r
9069 \r
9070 \r
9071 int\r
9072 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9073 {\r
9074   char cmdLine[MSG_SIZ];\r
9075 \r
9076   if (port[0] == NULLCHAR) {\r
9077     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9078   } else {\r
9079     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9080   }\r
9081   return StartChildProcess(cmdLine, "", pr);\r
9082 }\r
9083 \r
9084 \r
9085 /* Code to open TCP sockets */\r
9086 \r
9087 int\r
9088 OpenTCP(char *host, char *port, ProcRef *pr)\r
9089 {\r
9090   ChildProc *cp;\r
9091   int err;\r
9092   SOCKET s;\r
9093   struct sockaddr_in sa, mysa;\r
9094   struct hostent FAR *hp;\r
9095   unsigned short uport;\r
9096   WORD wVersionRequested;\r
9097   WSADATA wsaData;\r
9098 \r
9099   /* Initialize socket DLL */\r
9100   wVersionRequested = MAKEWORD(1, 1);\r
9101   err = WSAStartup(wVersionRequested, &wsaData);\r
9102   if (err != 0) return err;\r
9103 \r
9104   /* Make socket */\r
9105   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9106     err = WSAGetLastError();\r
9107     WSACleanup();\r
9108     return err;\r
9109   }\r
9110 \r
9111   /* Bind local address using (mostly) don't-care values.\r
9112    */\r
9113   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9114   mysa.sin_family = AF_INET;\r
9115   mysa.sin_addr.s_addr = INADDR_ANY;\r
9116   uport = (unsigned short) 0;\r
9117   mysa.sin_port = htons(uport);\r
9118   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9119       == SOCKET_ERROR) {\r
9120     err = WSAGetLastError();\r
9121     WSACleanup();\r
9122     return err;\r
9123   }\r
9124 \r
9125   /* Resolve remote host name */\r
9126   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9127   if (!(hp = gethostbyname(host))) {\r
9128     unsigned int b0, b1, b2, b3;\r
9129 \r
9130     err = WSAGetLastError();\r
9131 \r
9132     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9133       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9134       hp->h_addrtype = AF_INET;\r
9135       hp->h_length = 4;\r
9136       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9137       hp->h_addr_list[0] = (char *) malloc(4);\r
9138       hp->h_addr_list[0][0] = (char) b0;\r
9139       hp->h_addr_list[0][1] = (char) b1;\r
9140       hp->h_addr_list[0][2] = (char) b2;\r
9141       hp->h_addr_list[0][3] = (char) b3;\r
9142     } else {\r
9143       WSACleanup();\r
9144       return err;\r
9145     }\r
9146   }\r
9147   sa.sin_family = hp->h_addrtype;\r
9148   uport = (unsigned short) atoi(port);\r
9149   sa.sin_port = htons(uport);\r
9150   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9151 \r
9152   /* Make connection */\r
9153   if (connect(s, (struct sockaddr *) &sa,\r
9154               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9155     err = WSAGetLastError();\r
9156     WSACleanup();\r
9157     return err;\r
9158   }\r
9159 \r
9160   /* Prepare return value */\r
9161   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9162   cp->kind = CPSock;\r
9163   cp->sock = s;\r
9164   *pr = (ProcRef *) cp;\r
9165 \r
9166   return NO_ERROR;\r
9167 }\r
9168 \r
9169 int\r
9170 OpenCommPort(char *name, ProcRef *pr)\r
9171 {\r
9172   HANDLE h;\r
9173   COMMTIMEOUTS ct;\r
9174   ChildProc *cp;\r
9175   char fullname[MSG_SIZ];\r
9176 \r
9177   if (*name != '\\')\r
9178     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9179   else\r
9180     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9181 \r
9182   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9183                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9184   if (h == (HANDLE) -1) {\r
9185     return GetLastError();\r
9186   }\r
9187   hCommPort = h;\r
9188 \r
9189   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9190 \r
9191   /* Accumulate characters until a 100ms pause, then parse */\r
9192   ct.ReadIntervalTimeout = 100;\r
9193   ct.ReadTotalTimeoutMultiplier = 0;\r
9194   ct.ReadTotalTimeoutConstant = 0;\r
9195   ct.WriteTotalTimeoutMultiplier = 0;\r
9196   ct.WriteTotalTimeoutConstant = 0;\r
9197   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9198 \r
9199   /* Prepare return value */\r
9200   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9201   cp->kind = CPComm;\r
9202   cp->hFrom = h;\r
9203   cp->hTo = h;\r
9204   *pr = (ProcRef *) cp;\r
9205 \r
9206   return NO_ERROR;\r
9207 }\r
9208 \r
9209 int\r
9210 OpenLoopback(ProcRef *pr)\r
9211 {\r
9212   DisplayFatalError(_("Not implemented"), 0, 1);\r
9213   return NO_ERROR;\r
9214 }\r
9215 \r
9216 \r
9217 int\r
9218 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9219 {\r
9220   ChildProc *cp;\r
9221   int err;\r
9222   SOCKET s, s2, s3;\r
9223   struct sockaddr_in sa, mysa;\r
9224   struct hostent FAR *hp;\r
9225   unsigned short uport;\r
9226   WORD wVersionRequested;\r
9227   WSADATA wsaData;\r
9228   int fromPort;\r
9229   char stderrPortStr[MSG_SIZ];\r
9230 \r
9231   /* Initialize socket DLL */\r
9232   wVersionRequested = MAKEWORD(1, 1);\r
9233   err = WSAStartup(wVersionRequested, &wsaData);\r
9234   if (err != 0) return err;\r
9235 \r
9236   /* Resolve remote host name */\r
9237   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9238   if (!(hp = gethostbyname(host))) {\r
9239     unsigned int b0, b1, b2, b3;\r
9240 \r
9241     err = WSAGetLastError();\r
9242 \r
9243     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9244       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9245       hp->h_addrtype = AF_INET;\r
9246       hp->h_length = 4;\r
9247       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9248       hp->h_addr_list[0] = (char *) malloc(4);\r
9249       hp->h_addr_list[0][0] = (char) b0;\r
9250       hp->h_addr_list[0][1] = (char) b1;\r
9251       hp->h_addr_list[0][2] = (char) b2;\r
9252       hp->h_addr_list[0][3] = (char) b3;\r
9253     } else {\r
9254       WSACleanup();\r
9255       return err;\r
9256     }\r
9257   }\r
9258   sa.sin_family = hp->h_addrtype;\r
9259   uport = (unsigned short) 514;\r
9260   sa.sin_port = htons(uport);\r
9261   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9262 \r
9263   /* Bind local socket to unused "privileged" port address\r
9264    */\r
9265   s = INVALID_SOCKET;\r
9266   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9267   mysa.sin_family = AF_INET;\r
9268   mysa.sin_addr.s_addr = INADDR_ANY;\r
9269   for (fromPort = 1023;; fromPort--) {\r
9270     if (fromPort < 0) {\r
9271       WSACleanup();\r
9272       return WSAEADDRINUSE;\r
9273     }\r
9274     if (s == INVALID_SOCKET) {\r
9275       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9276         err = WSAGetLastError();\r
9277         WSACleanup();\r
9278         return err;\r
9279       }\r
9280     }\r
9281     uport = (unsigned short) fromPort;\r
9282     mysa.sin_port = htons(uport);\r
9283     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9284         == SOCKET_ERROR) {\r
9285       err = WSAGetLastError();\r
9286       if (err == WSAEADDRINUSE) continue;\r
9287       WSACleanup();\r
9288       return err;\r
9289     }\r
9290     if (connect(s, (struct sockaddr *) &sa,\r
9291       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9292       err = WSAGetLastError();\r
9293       if (err == WSAEADDRINUSE) {\r
9294         closesocket(s);\r
9295         s = -1;\r
9296         continue;\r
9297       }\r
9298       WSACleanup();\r
9299       return err;\r
9300     }\r
9301     break;\r
9302   }\r
9303 \r
9304   /* Bind stderr local socket to unused "privileged" port address\r
9305    */\r
9306   s2 = INVALID_SOCKET;\r
9307   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9308   mysa.sin_family = AF_INET;\r
9309   mysa.sin_addr.s_addr = INADDR_ANY;\r
9310   for (fromPort = 1023;; fromPort--) {\r
9311     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9312     if (fromPort < 0) {\r
9313       (void) closesocket(s);\r
9314       WSACleanup();\r
9315       return WSAEADDRINUSE;\r
9316     }\r
9317     if (s2 == INVALID_SOCKET) {\r
9318       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9319         err = WSAGetLastError();\r
9320         closesocket(s);\r
9321         WSACleanup();\r
9322         return err;\r
9323       }\r
9324     }\r
9325     uport = (unsigned short) fromPort;\r
9326     mysa.sin_port = htons(uport);\r
9327     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9328         == SOCKET_ERROR) {\r
9329       err = WSAGetLastError();\r
9330       if (err == WSAEADDRINUSE) continue;\r
9331       (void) closesocket(s);\r
9332       WSACleanup();\r
9333       return err;\r
9334     }\r
9335     if (listen(s2, 1) == SOCKET_ERROR) {\r
9336       err = WSAGetLastError();\r
9337       if (err == WSAEADDRINUSE) {\r
9338         closesocket(s2);\r
9339         s2 = INVALID_SOCKET;\r
9340         continue;\r
9341       }\r
9342       (void) closesocket(s);\r
9343       (void) closesocket(s2);\r
9344       WSACleanup();\r
9345       return err;\r
9346     }\r
9347     break;\r
9348   }\r
9349   prevStderrPort = fromPort; // remember port used\r
9350   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9351 \r
9352   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9353     err = WSAGetLastError();\r
9354     (void) closesocket(s);\r
9355     (void) closesocket(s2);\r
9356     WSACleanup();\r
9357     return err;\r
9358   }\r
9359 \r
9360   if (send(s, UserName(), strlen(UserName()) + 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   if (*user == NULLCHAR) user = UserName();\r
9368   if (send(s, user, strlen(user) + 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 (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9376     err = WSAGetLastError();\r
9377     (void) closesocket(s);\r
9378     (void) closesocket(s2);\r
9379     WSACleanup();\r
9380     return err;\r
9381   }\r
9382 \r
9383   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\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   (void) closesocket(s2);  /* Stop listening */\r
9391 \r
9392   /* Prepare return value */\r
9393   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9394   cp->kind = CPRcmd;\r
9395   cp->sock = s;\r
9396   cp->sock2 = s3;\r
9397   *pr = (ProcRef *) cp;\r
9398 \r
9399   return NO_ERROR;\r
9400 }\r
9401 \r
9402 \r
9403 InputSourceRef\r
9404 AddInputSource(ProcRef pr, int lineByLine,\r
9405                InputCallback func, VOIDSTAR closure)\r
9406 {\r
9407   InputSource *is, *is2 = NULL;\r
9408   ChildProc *cp = (ChildProc *) pr;\r
9409 \r
9410   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9411   is->lineByLine = lineByLine;\r
9412   is->func = func;\r
9413   is->closure = closure;\r
9414   is->second = NULL;\r
9415   is->next = is->buf;\r
9416   if (pr == NoProc) {\r
9417     is->kind = CPReal;\r
9418     consoleInputSource = is;\r
9419   } else {\r
9420     is->kind = cp->kind;\r
9421     /* \r
9422         [AS] Try to avoid a race condition if the thread is given control too early:\r
9423         we create all threads suspended so that the is->hThread variable can be\r
9424         safely assigned, then let the threads start with ResumeThread.\r
9425     */\r
9426     switch (cp->kind) {\r
9427     case CPReal:\r
9428       is->hFile = cp->hFrom;\r
9429       cp->hFrom = NULL; /* now owned by InputThread */\r
9430       is->hThread =\r
9431         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9432                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9433       break;\r
9434 \r
9435     case CPComm:\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) InputThread,\r
9440                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9441       break;\r
9442 \r
9443     case CPSock:\r
9444       is->sock = cp->sock;\r
9445       is->hThread =\r
9446         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9447                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9448       break;\r
9449 \r
9450     case CPRcmd:\r
9451       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9452       *is2 = *is;\r
9453       is->sock = cp->sock;\r
9454       is->second = is2;\r
9455       is2->sock = cp->sock2;\r
9456       is2->second = is2;\r
9457       is->hThread =\r
9458         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9459                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9460       is2->hThread =\r
9461         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9462                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9463       break;\r
9464     }\r
9465 \r
9466     if( is->hThread != NULL ) {\r
9467         ResumeThread( is->hThread );\r
9468     }\r
9469 \r
9470     if( is2 != NULL && is2->hThread != NULL ) {\r
9471         ResumeThread( is2->hThread );\r
9472     }\r
9473   }\r
9474 \r
9475   return (InputSourceRef) is;\r
9476 }\r
9477 \r
9478 void\r
9479 RemoveInputSource(InputSourceRef isr)\r
9480 {\r
9481   InputSource *is;\r
9482 \r
9483   is = (InputSource *) isr;\r
9484   is->hThread = NULL;  /* tell thread to stop */\r
9485   CloseHandle(is->hThread);\r
9486   if (is->second != NULL) {\r
9487     is->second->hThread = NULL;\r
9488     CloseHandle(is->second->hThread);\r
9489   }\r
9490 }\r
9491 \r
9492 int no_wrap(char *message, int count)\r
9493 {\r
9494     ConsoleOutput(message, count, FALSE);\r
9495     return count;\r
9496 }\r
9497 \r
9498 int\r
9499 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9500 {\r
9501   DWORD dOutCount;\r
9502   int outCount = SOCKET_ERROR;\r
9503   ChildProc *cp = (ChildProc *) pr;\r
9504   static OVERLAPPED ovl;\r
9505   static int line = 0;\r
9506 \r
9507   if (pr == NoProc)\r
9508   {\r
9509     if (appData.noJoin || !appData.useInternalWrap)\r
9510       return no_wrap(message, count);\r
9511     else\r
9512     {\r
9513       int width = get_term_width();\r
9514       int len = wrap(NULL, message, count, width, &line);\r
9515       char *msg = malloc(len);\r
9516       int dbgchk;\r
9517 \r
9518       if (!msg)\r
9519         return no_wrap(message, count);\r
9520       else\r
9521       {\r
9522         dbgchk = wrap(msg, message, count, width, &line);\r
9523         if (dbgchk != len && appData.debugMode)\r
9524             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9525         ConsoleOutput(msg, len, FALSE);\r
9526         free(msg);\r
9527         return len;\r
9528       }\r
9529     }\r
9530   }\r
9531 \r
9532   if (ovl.hEvent == NULL) {\r
9533     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9534   }\r
9535   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9536 \r
9537   switch (cp->kind) {\r
9538   case CPSock:\r
9539   case CPRcmd:\r
9540     outCount = send(cp->sock, message, count, 0);\r
9541     if (outCount == SOCKET_ERROR) {\r
9542       *outError = WSAGetLastError();\r
9543     } else {\r
9544       *outError = NO_ERROR;\r
9545     }\r
9546     break;\r
9547 \r
9548   case CPReal:\r
9549     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9550                   &dOutCount, NULL)) {\r
9551       *outError = NO_ERROR;\r
9552       outCount = (int) dOutCount;\r
9553     } else {\r
9554       *outError = GetLastError();\r
9555     }\r
9556     break;\r
9557 \r
9558   case CPComm:\r
9559     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9560                             &dOutCount, &ovl);\r
9561     if (*outError == NO_ERROR) {\r
9562       outCount = (int) dOutCount;\r
9563     }\r
9564     break;\r
9565   }\r
9566   return outCount;\r
9567 }\r
9568 \r
9569 int\r
9570 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9571                        long msdelay)\r
9572 {\r
9573   /* Ignore delay, not implemented for WinBoard */\r
9574   return OutputToProcess(pr, message, count, outError);\r
9575 }\r
9576 \r
9577 \r
9578 void\r
9579 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9580                         char *buf, int count, int error)\r
9581 {\r
9582   DisplayFatalError(_("Not implemented"), 0, 1);\r
9583 }\r
9584 \r
9585 /* see wgamelist.c for Game List functions */\r
9586 /* see wedittags.c for Edit Tags functions */\r
9587 \r
9588 \r
9589 VOID\r
9590 ICSInitScript()\r
9591 {\r
9592   FILE *f;\r
9593   char buf[MSG_SIZ];\r
9594   char *dummy;\r
9595 \r
9596   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9597     f = fopen(buf, "r");\r
9598     if (f != NULL) {\r
9599       ProcessICSInitScript(f);\r
9600       fclose(f);\r
9601     }\r
9602   }\r
9603 }\r
9604 \r
9605 \r
9606 VOID\r
9607 StartAnalysisClock()\r
9608 {\r
9609   if (analysisTimerEvent) return;\r
9610   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9611                                         (UINT) 2000, NULL);\r
9612 }\r
9613 \r
9614 VOID\r
9615 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9616 {\r
9617   highlightInfo.sq[0].x = fromX;\r
9618   highlightInfo.sq[0].y = fromY;\r
9619   highlightInfo.sq[1].x = toX;\r
9620   highlightInfo.sq[1].y = toY;\r
9621 }\r
9622 \r
9623 VOID\r
9624 ClearHighlights()\r
9625 {\r
9626   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9627     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9628 }\r
9629 \r
9630 VOID\r
9631 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9632 {\r
9633   premoveHighlightInfo.sq[0].x = fromX;\r
9634   premoveHighlightInfo.sq[0].y = fromY;\r
9635   premoveHighlightInfo.sq[1].x = toX;\r
9636   premoveHighlightInfo.sq[1].y = toY;\r
9637 }\r
9638 \r
9639 VOID\r
9640 ClearPremoveHighlights()\r
9641 {\r
9642   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9643     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9644 }\r
9645 \r
9646 VOID\r
9647 ShutDownFrontEnd()\r
9648 {\r
9649   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9650   DeleteClipboardTempFiles();\r
9651 }\r
9652 \r
9653 void\r
9654 BoardToTop()\r
9655 {\r
9656     if (IsIconic(hwndMain))\r
9657       ShowWindow(hwndMain, SW_RESTORE);\r
9658 \r
9659     SetActiveWindow(hwndMain);\r
9660 }\r
9661 \r
9662 /*\r
9663  * Prototypes for animation support routines\r
9664  */\r
9665 static void ScreenSquare(int column, int row, POINT * pt);\r
9666 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9667      POINT frames[], int * nFrames);\r
9668 \r
9669 \r
9670 #define kFactor 4\r
9671 \r
9672 void\r
9673 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9674 {       // [HGM] atomic: animate blast wave\r
9675         int i;\r
9676 \r
9677         explodeInfo.fromX = fromX;\r
9678         explodeInfo.fromY = fromY;\r
9679         explodeInfo.toX = toX;\r
9680         explodeInfo.toY = toY;\r
9681         for(i=1; i<4*kFactor; i++) {\r
9682             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9683             DrawPosition(FALSE, board);\r
9684             Sleep(appData.animSpeed);\r
9685         }\r
9686         explodeInfo.radius = 0;\r
9687         DrawPosition(TRUE, board);\r
9688 }\r
9689 \r
9690 void\r
9691 AnimateMove(board, fromX, fromY, toX, toY)\r
9692      Board board;\r
9693      int fromX;\r
9694      int fromY;\r
9695      int toX;\r
9696      int toY;\r
9697 {\r
9698   ChessSquare piece;\r
9699   POINT start, finish, mid;\r
9700   POINT frames[kFactor * 2 + 1];\r
9701   int nFrames, n;\r
9702 \r
9703   if (!appData.animate) return;\r
9704   if (doingSizing) return;\r
9705   if (fromY < 0 || fromX < 0) return;\r
9706   piece = board[fromY][fromX];\r
9707   if (piece >= EmptySquare) return;\r
9708 \r
9709   ScreenSquare(fromX, fromY, &start);\r
9710   ScreenSquare(toX, toY, &finish);\r
9711 \r
9712   /* All moves except knight jumps move in straight line */\r
9713   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9714     mid.x = start.x + (finish.x - start.x) / 2;\r
9715     mid.y = start.y + (finish.y - start.y) / 2;\r
9716   } else {\r
9717     /* Knight: make straight movement then diagonal */\r
9718     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9719        mid.x = start.x + (finish.x - start.x) / 2;\r
9720        mid.y = start.y;\r
9721      } else {\r
9722        mid.x = start.x;\r
9723        mid.y = start.y + (finish.y - start.y) / 2;\r
9724      }\r
9725   }\r
9726   \r
9727   /* Don't use as many frames for very short moves */\r
9728   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9729     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9730   else\r
9731     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9732 \r
9733   animInfo.from.x = fromX;\r
9734   animInfo.from.y = fromY;\r
9735   animInfo.to.x = toX;\r
9736   animInfo.to.y = toY;\r
9737   animInfo.lastpos = start;\r
9738   animInfo.piece = piece;\r
9739   for (n = 0; n < nFrames; n++) {\r
9740     animInfo.pos = frames[n];\r
9741     DrawPosition(FALSE, NULL);\r
9742     animInfo.lastpos = animInfo.pos;\r
9743     Sleep(appData.animSpeed);\r
9744   }\r
9745   animInfo.pos = finish;\r
9746   DrawPosition(FALSE, NULL);\r
9747   animInfo.piece = EmptySquare;\r
9748   Explode(board, fromX, fromY, toX, toY);\r
9749 }\r
9750 \r
9751 /*      Convert board position to corner of screen rect and color       */\r
9752 \r
9753 static void\r
9754 ScreenSquare(column, row, pt)\r
9755      int column; int row; POINT * pt;\r
9756 {\r
9757   if (flipView) {\r
9758     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9759     pt->y = lineGap + row * (squareSize + lineGap);\r
9760   } else {\r
9761     pt->x = lineGap + column * (squareSize + lineGap);\r
9762     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9763   }\r
9764 }\r
9765 \r
9766 /*      Generate a series of frame coords from start->mid->finish.\r
9767         The movement rate doubles until the half way point is\r
9768         reached, then halves back down to the final destination,\r
9769         which gives a nice slow in/out effect. The algorithmn\r
9770         may seem to generate too many intermediates for short\r
9771         moves, but remember that the purpose is to attract the\r
9772         viewers attention to the piece about to be moved and\r
9773         then to where it ends up. Too few frames would be less\r
9774         noticeable.                                             */\r
9775 \r
9776 static void\r
9777 Tween(start, mid, finish, factor, frames, nFrames)\r
9778      POINT * start; POINT * mid;\r
9779      POINT * finish; int factor;\r
9780      POINT frames[]; int * nFrames;\r
9781 {\r
9782   int n, fraction = 1, count = 0;\r
9783 \r
9784   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9785   for (n = 0; n < factor; n++)\r
9786     fraction *= 2;\r
9787   for (n = 0; n < factor; n++) {\r
9788     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9789     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9790     count ++;\r
9791     fraction = fraction / 2;\r
9792   }\r
9793   \r
9794   /* Midpoint */\r
9795   frames[count] = *mid;\r
9796   count ++;\r
9797   \r
9798   /* Slow out, stepping 1/2, then 1/4, ... */\r
9799   fraction = 2;\r
9800   for (n = 0; n < factor; n++) {\r
9801     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9802     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9803     count ++;\r
9804     fraction = fraction * 2;\r
9805   }\r
9806   *nFrames = count;\r
9807 }\r
9808 \r
9809 void\r
9810 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9811 {\r
9812     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9813 \r
9814     EvalGraphSet( first, last, current, pvInfoList );\r
9815 }\r
9816 \r
9817 void\r
9818 SettingsPopUp(ChessProgramState *cps)\r
9819 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9820       EngineOptionsPopup(savedHwnd, cps);\r
9821 }\r